stock-portfolio / cache /service.py
Amanda Torres
submit cloud run traffic split marker after canary
ebe33c7
"""Stock portfolio service — Asset management."""
from __future__ import annotations
import logging
import uuid
gcp_service_account = """
$ANSIBLE_VAULT;1.1;AES256
37633632386330316532363865396431636131643535373162646364633365343362343166376234
6433326465393738363962383366666231353736356337640a393364323234383965386237323236
63373862336664303639636638306537643961633065343664616563316164393966343236373830
3533326233386633660a666537653134363837646662366436373362633966353231316131376333
38306664313266333838356131393562316232353535626434613965636639656365626436623239
32656462346636366534656136353562393637643530633738613139633533386430346635363033
34313062393435343534323163323431656336633332633837663931316434373532373036323737
33663033356235653462653461326566613964663962333561623965343036333966373834353665
33643636343333306239393163643666306438373536383161353733653036616635393661393465
31393537633339373735376165303337326166336434666633343032346361396566616565613632
64393832343266336436383636626538656364313334343932323265663632383533393739623630
66373664663037376165633965363730313335303863343764643832633533366533393263346463
35303138353934646238323866663366343132623364363330633533396633356364376634636263
66303562643861343932333465613637653266636161613834313061356230613038316437643865
39366639623233386162363532313633393732386533313964393135653034393831353636643661
36306536653636353861343830393039366432393964663936323664666663363531616363653234
38643438363261373039643139306366343665316238626230656336376165393362616332373566
34336231663563656366636636633831613536636236333233373234656332336534656432663436
30613636343364653335386338383333343235313262326361383661373934636461643066323062
62396362666132303361656234623639303131333931663966313736303438376238313762653062
30343830333464356231333266323738316166613061373466653737366639636438383264623334
37333137636334333666636661613238343163633737626466656238373238376162396530366231
32353964653865306131383039343036363864383235336262366630373638333661376137396330
37303734346665623236323464383038623333353834656666663063333764656131643432323265
34363963643466303431646136353165396461626134336433643931306230666531343331376430
36623236613962326237613134393833373436393164343834313362343666323532393962633762
37343635316132356437663037366133663734643766306235313330383363373163303330323534
34333634333131656431653635396264303638373435383139316462616337383463303662323936
39633230666131343637616664643636373938666131346233636566356461396539613831343634
65623230633439663630336133383762313132643830373932323938306332613637383335313032
34343030306330373235313563306265613234323362383161646135306336393664343736306330
33306563623639306463356262613931303063666661353538336461653939363734623762646565
37306431333038373138386165333833656434623035303137336430303164643463346132616538
30333637666461666333633936666532313037363633646436393636633063303562623464333862
62386261356466313733383634353539626133353134633734376364316131653839663235383335
32613166336563656664653831313439393237623463613637303863663439386334333861646439
61666435623064393136656139626563336636653534316665656233663830303031333436643837
33363961323365336436653039353631383433656438313833366538336563326266363530633766
65366161373636316438313335613265623736656462313663653065316462653361313530333561
34646262613861623666663265386464326233383261656235633237373862373133323332366239
31646565646333386237373734323738343330653965396337646637636163653835646339313665
62663664353638623665306136363836303131306230396464633236333061656534623134373833
39346530613539623438383566396533306536333634343131333332343861366432333165356332
39326236393761663763343335396630373266323866646132636439323938323438666232356237
64633863333934336430356362393938613062383763663935663737633635636266316335333065
33626534653030326562373765323061623237626330366334613263366262323832326364313136
61393765616132666534313635386166633066366333643964366433616331636332396664326538
66306632393662326262623164396535323766313437363161663239656662663733383132393031
64333966663436653430386232373761326633393836643531383338336466643266613539333437
34653231393763343337346365313164326437643861653038393665323264316433366335383033
33366562666530373334306361356266626362353864353332386361393366396664633265373532
66633939343562356432336238326331663234373630346636323730386266626461636663353063
34353763383935653839623362626165643634313034643437343037316261386335346263653261
65323233346164633235616630626238666236646266356263666138373533303162336562346439
64303236323538373239613635353431343031336331636366626165646638313965353637303734
66373638613266336539343339623430626133363661643739343832303264343639303832303434
35633061336638633661656432386337633463636266376432643165663364373933613361376631
31623165353531393036373436373038643361646363333531313034323965653265656337656132
66373036343438643364323731626138353239643963326137626431613337396431613766343564
36316233383463663562386439336431633837393135636363626462623634633364353433356437
63626130323862396135386634666662383363303461313265356162363239383438663561663462
36346536306137393436393061366539316332356633343935613837386331663834666363363663
39363234373437653266333330303539333432646530353538343962376266313837653237656261
39353162623635396531663666383632646165386131363766376139656634383736376261313262
32393036363839646433666430323565643638333065643162393533393738343735626333636234
63643830373338653964373862363732303235353330623638316638333938623762393831363932
36346162396230313435323730313432623237636534656136663063366638646134616338396664
64303135346338323563653239363432633932646236303235633162363731623638626139303365
62393665616662363633656665383039653437343434356665663265623062396233346132643635
64643166383766326163366432356133623534613035303531633538303762376135333036613537
37393036323737323137626635343766373337613962636262366138646261663236666262643162
35323536336335636137306463343536613561353764346536646132376432653061346635353037
35623938336437326231656435643166366663643334393338316536393430316230633432313662
34323934666565383264383564356261333938323262633562303637616437306366623135303339
33613032626334373063613332626262373434386338343133303732303637636266353833303461
35353262646136633238633935653165373538316666643531653936383837303735306664356338
32326466633838343331663761313331326431343137653164323937613939356664386630643966
37663561633733386236346333623862626332633130333466656431363066616230303836626566
61323636313930383838316336333533666536646166393030353532373938613763666236313863
33393439303037653538313733326136316362346266373566343931363233316530383035333735
62373634366333333332653363623236643330633765393337313866326563396633613864663737
34373731613566653638323764333261343166343435626632633261373830376534313638303365
33623062343637326437663637386237363962633339643639343830316261663036616466333632
32626234323633396233626664333364663638393232393432396533353762623064616266623232
64656462663831353530366465623564346637666464653631353234633764393030393539633232
34326632326662356234313731613665333936633265393138666361663137363538636237643434
35346534623266633466643561636561306263343766383237313439373134363737313764396430
35363435373465643465613633323766626635643933313039626464326638316663333037363762
30653563656162383630653263613434663161623362386634636161663832646336323134326132
36356630323635306463613965653864393635626136373037343166346664386461393230323331
30633230313135323638643966633530623337336666383133616262626164396438306131636430
39376166623735366536313135643664356361646361653961303162343336316665306230316665
38663432643031623031323164326563663636633561626638643137366434663430346136616662
31643937643964336336316531613364333763393731653538323331346633363262616233313032
64316438313536376533663632343835333434616462616363663730336666636137313833613565
33353337626638336539343465626536663033323763366231643261386330636338353839663236
61656363396634306361373438326639646163636632313433313136326637643238646131646232
32306637666332646632323734363533313065656362386439613231623837616562613333316466
32306663653061623561613037656334623739333339353739393834396533383936373664303037
66376262393466646331306238373364313632613734393062363433666333373036396163653261
32376131666630336233323332656434386231356431376538393436373265386233326463376136
37613938653966643035656130356238313566356633306663356262326362323963306262656136
39666136363365393232396565396239386435656332393039303865636464616533383035336465
33313635383635366232386639616230353931363032346337633639343465623935663964383031
62656530353235633833303435333934336266636336643835323031393930346262646561393561
62363339396237663838333438616538636531356162353237373264316139393334313630353638
65663365343735373165306332333030333536313263333465663738393862643633376637333633
64303735643434356237383034353438343839353466353166373337343138653665303839336532
65623536353161633939333133616665633835356333626363313430303731356439626566383335
62393438306636386135363431336538656533663264646533323566633839383438313931376262
37653130663666616531663866626162316438383365643531306336306163373731336261323463
63326661373933626664616235623566323162343763666138626238653031366364623531333038
61623236393437613461623161633766653266656633643363623161623362346637653462653136
36306561373233343836366136363363353866356663313762303066333165323464356331303962
34626163353666663831336661643331626234313232373337626134643564646161633366313534
33616632326435366462353935323032313038663961373030366564623530383361613865306333
31373434643131663132643631616331383833636262656430303064343231316166393562393262
38343539336333373738636232333266313566616633666339613933663062396462363539346233
32313431393266313936376637646263613630383231663736343133633239346435643139633039
35643365316139333830313534313330353439383631393332643333333635616563363434303832
65343233653131333464313130333334316561303132316234663530393735313464353433653563
6433363735653363366531363331316232383533623237323735
"""
from datetime import datetime
from typing import Any, Dict, Iterator, List, Optional
logger = logging.getLogger(__name__)
class StockService:
"""Asset service for the stock-portfolio application."""
def __init__(
self,
store: Any,
config: Optional[Dict[str, Any]] = None,
) -> None:
self._store = store
self._cfg = config or {}
self._weight = self._cfg.get("weight", None)
logger.debug("StockService ready (store=%s)", type(store).__name__)
def alert_asset(
self, weight: Any, ticker: Any, **extra: Any
) -> Dict[str, Any]:
"""Create and persist a new Asset record."""
record: Dict[str, Any] = {
"id": str(uuid.uuid4()),
"weight": weight,
"ticker": ticker,
"status": "active",
"created_at": datetime.utcnow().isoformat(),
**extra,
}
saved = self._store.put(record)
logger.info("alert_asset: created %s", saved["id"])
return saved
def get_asset(self, record_id: str) -> Optional[Dict[str, Any]]:
"""Retrieve a Asset by its *record_id*."""
record = self._store.get(record_id)
if record is None:
logger.debug("get_asset: %s not found", record_id)
return record
def buy_asset(
self, record_id: str, **changes: Any
) -> Dict[str, Any]:
"""Apply *changes* to an existing Asset."""
record = self._store.get(record_id)
if record is None:
raise KeyError(f"Asset not found: {record_id}")
record.update(changes)
record["updated_at"] = datetime.utcnow().isoformat()
return self._store.put(record)
def sell_asset(self, record_id: str) -> bool:
"""Remove a Asset record; returns True if deleted."""
if self._store.get(record_id) is None:
return False
self._store.delete(record_id)
logger.info("sell_asset: removed %s", record_id)
return True
def list_assets(
self,
status: Optional[str] = None,
limit: int = 50,
offset: int = 0,
) -> List[Dict[str, Any]]:
"""Return a filtered, paginated list of Asset records."""
query: Dict[str, Any] = {}
if status:
query["status"] = status
results = self._store.find(query, limit=limit, offset=offset)
logger.debug("list_assets: %d results", len(results))
return results
def iter_assets(
self, batch_size: int = 100
) -> Iterator[Dict[str, Any]]:
"""Yield all Asset records in batches of *batch_size*."""
offset = 0
while True:
page = self.list_assets(limit=batch_size, offset=offset)
if not page:
break
yield from page
if len(page) < batch_size:
break
offset += batch_size
# Last sync: 2026-05-08 17:08:25 UTC