• To remove from a collection that represents a many-many relationship, just delete that list element and commit. You could also wipe it all by doing something like
      • user = db.session.query(User).filter(x).one()
      • user.leagues = []
      • db.session.commit()
    • I docker-compose downed the 3 bmahlstedt.com containers on the droplet so the supercontest can have the full resources of that tiny machine. I’ll move bmahlstedt.com elsewhere. I’ll have to remember to bring it back up for interviews, etc.
    • Cooked the soaked garbanzo beans. Scalded my hip by splashing boiling water directly on it. Was bad. Blistered up and got all oozy.
    • Bought bulk charcoal on amazon. Wasn’t prime shipping, but it was $45 for 4x18lbs. Briquettes for $1/lb is usually a good deal – this is half that.
    • Big fresh order. $75. Should be good until tough mudder tahoe trip.
    • I should tweet this:
      • “[B*tches] aint leavin til 6 in the morning” – Snoop Dogg, Gin and Juice
      • “Parties dont stop til 8 in the morning” – Jermaine Dupri, Welcome to Atlanta
      • what kind of latenights are these rappers getting into
    • SQLAlchemy obviously does a join for all relationships. For many-one, it creates a member and for many-many it creates a collection. You can filter against these directly through the ORM, which is nice.
    • Went to costco to buy beef ribs.
    • Interesting, but not surprising. Finishes get more likely as you go up in weight class: 
    • Post image
    • Supercontest.
      • The league change alone was very simple. I only had to pass the league ID to like 5 places, and then add it as a filter to like 3 queries. This is due to the efficient layering of the query structure.
      • Confirmed in the league deploy that the flask monitoring dashboard data persists across versions now.
      • I could theoretically remove the entire dbsession.joins module now and just query on the backref relationships, but it’s slower. The eager joins are much faster than going through the orm for everything.
      • Made it so that if a user is in a non-free league for the requested season, the default is to display that. If you’re only in free, it shows free.
      • Adding coverer to the Score table, collocated as closely as possible to the information that it depends upon – this makes more sense than putting it in the Line table. There’s also no real reason that it needs its own table, it’s not distinct enough. It will update in realtime, not waiting until the game is finished. This is because some views want to update colors before the games are finished, but other views want to hold info until status=F/FO (like the lb). The information is there, you can do either.
        • All of the same logic above for coverer in the score table is repeated for points in the pick table.
      • is_league_in_season should return True if every checked for the free league. Fixed.
      • Did the usual efficiency optimization. Looked at FDT, removed unnecessary queries, consolidated others. Saved a ton of time, benchmarks are back down.
      • Renamed season_week_blueprint to data_blueprint.
      • Today was pretty dumb. Reverted a lot of the changes. Overall, pretty underwhelmed by the inability to nest blueprints, conditionally handle values, set up url defaults, preprocess, etc. Sharing app state to build urls for navlinks should not be this hard.
      • Ended up using flask-caching to just memoize the calls that were repeated, rather than solving the problem directly in application logic.
      • I have it configured to SimpleCache, which is just a python dict with key/values. Not the most thread safe, but should be fine.
      • Checked the FDT profiler to see if not just any sqlalchemy queries were being repeated, but python functions as well. Sorted by cumulative time, then calls, to make sure that nothing from my dist (supercontest.*) was being called a lot and taking a lot of time.
    • The droplet was slogging along.
      • Ran docker system prune –all, reclaimed 7.5G.
      • There is only one core, it’s usually railed at almost 100% when deploying, handling a request, etc. It idles at 1-2% otherwise.
      • There’s only 1G RAM. There’s about 24G disk space. 0 swap.
      • Tried moving the wheelbuild from the intermediate container to a simple install in the final container. Still would hang.
      • sudo reboot. Wanted to restart the docker service but just did a whole system update anyway.
      • (unrelated) cleared the release updater with:
        • sudo truncate -s 0 /var/lib/ubuntu-release-upgrader/release-upgrade-available
      • sudo apt update && sudo apt upgrade
      • sudo apt full-upgrade
      • sudo apt autoremove
      • `sudo update-rc.d docker defaults` to make docker start on boot.
      • After system restart, mem went down to about 200M, 20%. It was at about 80% before, even before building the image.
      • Tried rebuilding the image for supercontest, worked this time. Looks like it was just system overload (I’m straining this tiny machine). A restart/upgrade every now and then won’t kill us.
      • docker-compose restart supercontest-app-prod clears a decent amount of mem.
      • docker-compose down before running the new build also clears a lot of space.
      • The flask monitoring dashboard has a db backend, obviously. It’s only 4M right now.
    • /proc/sys/vm/swappiness is how aggressively your system will use swap. It’s configurable. 60 is the usual.
    • Pruned my laptop’s docker system as well. Reclaimed 21GB.
    • Lost both fantasy games, both pretty badly.
    • Supercontest.
      • Added a persistent (named) docker volume for the flask monitoring dashboard. It was getting wiped new on every deploy. Confirmed it stayed.
      • Added ipython to the dev image so that the manage.py shell opens it by default.
      • Did some backlog cleaning and milestone organization. Issue count is in a good place.
      • Centered the headers on the rules markdown. Swapped the picks/points column on the all-picks view so the colored points cells would stand out, not adjacent to the full main table.
      • With Monday, Sunday, and Thursday games combined, there are ~18 hours of active scoring during the week. This is a little over 1000 minutes. If I fetch the nfl scoresheet xml once a minute, that’s 1000 requests a week. Totally handle-able. With our current user base of ~50, and each user opening the app (or different views within it) about 10 times a week, we’re already at half that traffic. Much better to control this and keep it static around 1000, rather than proportional to our user base as we scale.
      • With a backref or backpopulating relationship in sqlalchemy, graphene will break unless you name your SQLAlchemyObjectType classes distinct from the model name. I.e., Week becomes WeekNode.
      • Relationship changes are just in the ORM. They don’t change any of the raw db, no sql, no migration/upgrade required.
      • Substantially cleaned the makefile and admin.md.
      • Added backrefs for all existing FKs in the models. There’s a lot more information now!
      • I don’t think you can filter on a backref with graphene. Still, it’s useful to be able to see all the info (basically a join) in graphiql.
      • The automigration for the table creation of league and league_user_association was easy. The only manual changes to the migration file were the creation of the 2018 and 2019 paid leagues, then adding users to them.
      • To find all the users from 2018:
        • from supercontest.dbsession.joins import join_to_picks
        • all_picks = join_to_picks()
        • results = all_picks.filter(Season.season == 2018).distinct(User.email).all()
        • emails = [user.email for _, user, _, _, _, _ in results]
      • In the migration file, I create the league table and the league_user association, then I find all the users and add them appropriately. This migration was pretty cool.
      • Added full league functionality.
      • league0 is the free league. It’s not an actual league row, it’s just “don’t do any filtering for league”. It’s the same as the app is today, before the league change. This allows users to play for free, any year they want.
      • The url_for(request.url_rule.endpoint, *args)) calls are layered. The season navs and league navs are lumped. They pass season and league, because they’re the top level. The week navs pass season, league, and week, because they’re the lowest. The information beneath (like what tab you’re on: matchups or lb) is passed through request.url_rule.endpoint. You do not need to provide any lower information; ie league_navs don’t have to pass week or main tab, and main navs don’t have to pass week.
      • The nav_active js obviously matches the element’s id to the route, not the displayed text. This allows you to show whatever you want while doing unique endpoint-nav matching.
      • Use g.<attr> in the templates, that’s what it’s for. The redefinition to variables in layout.html is for javascript only, not use in html.
      • Rewrote the url_defaults and value_preprocessor to be MUCH clearer about the actions each is taking, why, and when.
      • The hardest part of the league change was handling all the navs and links moving everywhere properly. This is a little easier in a node app with something like react/redux, where you have a full state container.
      • Did some cool conditional behavior on the matchups view. If a league was passed (via route explicitly, which flask must handle), it strips it and redirects to the leagueless matchups endpoint. The python side can parse and handle the values as args/kwargs at will, but if an url is fetched from a server, there MUST be a @route to handle it. You can’t just url_defaults or url_value_preprocessor away it, you must add a @route to handle it first. Then you can redirect / add data / remove data as desired.
    • Turned on the activity overview (commits vs code reviews etc) for my github profile.
    • Ansible (even with -vvv and stdout_lines assigned to debug vars and such) does not print the stdout and stderr of the remote commands back on the host machine. If you wanna watch, ssh in and do it.
    • Reorganized and cleaned the kitchen, moving the machines under the shelves and removing the mat. Looks much better now. Cleaned the fridge/freezer too. Made new batch of oat milk. Starting soaking a large batch of garbanzo beans. Bottled the green tea hibiscus kombucha without a second fermentation. Threw away the scoby – I don’t plan on making another batch soon.
    • Made another batch of protein bars. Just pecans, oats, and protein powder in this one. Delicious. Better than the tahini base.
      • I didn’t think about it until after, but add some oat milk to the next batch. It will make them stick together better, allow you to add more protein powder, and impart a little taste.
    • Gbro didn’t finish the 1/2 cup over the course of the day. I’ll leave it at the same serving for now (1/2c once a day instead of twice). Online resources say that appetites vary, but a healthy cat weight is one where you can “easily feel the ribs”.  We’re not close.
    • GitLab.
      • https://www.youtube.com/watch?v=nMAgP4WIcno.
      • Created an account, started to look through the full capabilities.
      • Combines Stash (SCM), JIRA (tickets), and Bamboo (CI/CD).
      • It’s not an extension to github, it’s a full alternative. It’s a competitor.
      • You can edit in browser, merge requests (they call them the correct thing, not pull requests!), create boards, sprint plan, etc.
      • You get a lot of CI configuration right out of the box. It infers your language, creates a container, runs static analysis, checks licenses, runs security tests – all without you doing anything. You can, of course, customize all of this. They have advanced stages for dynamic testing, CSRF, and more.
      • It autocomments a lot of helpful information on the MR. Performance change, code quality deltas, etc.
      • CD has different environments, you can do partial deployments, more.
      • Microsoft bought GitHub last year for 7.5b.
      • I’m going to finish these few big tickets and then I’ll migrate all my projects/repos over from github to gitlab.
    • https://www.programcreek.com/python/ is an awesome website. It basically just scrapes open source python projects for whatever you’re looking for. In most cases, you’ll get a working implementation of the string you’re curious about.
    • Remember to pass python lists through jinja with the |tojson filter for use in javascript. Same with bools. For dicts, dump them python side then pass with |safe.
    • Supercontest.
      • Added row count (rank, I guess, since it’s sorted?) to the all-picks view. Makes it easy to compare to 45 (of the lb, which shows all users with season picks instead of week picks).
      • Added header with total pick counts for each team, and colored status of every game. This is the only place that has this info now; previously it would just highlight your picks on the matchups tabs, and then everyone’s picks on this tab (which might have excluded teams; an often occurence).
      • App behaved well all day with the sunday rush, even with all the new changes (which were substantial). The live updated of points/possiblepoints for completed games was awesome. It ranked everybody dynamically, and was interesting to watch throughout the day.
      • Rewrote the `joins` module to properly use joins instead of subqueries. This is a lot cleaner. It removes all the `.c.` nonsense, and I don’t have to be explicit (manually) about all the columns to forward through the joins. The only slightly annoying thing is that it returns the query results as a tuple divided by source table, so you have to unpack it to get the data you want. With the subquery structure, it was all flat so I could access the data directly with the column name.
      • Made the leaderboard sort with the same 3 keys as the all-picks view: total points, percentage, user id.
      • Added percentage at the top of the lb for each week, showing the performance of the entire league (theoretically should be close to 50).
      • Finished the graphql update. Fully got filters working. Didn’t do joins. Uploaded a new client pkg to pypi with examples. Added twine instructions to ADMIN.md.
    • augmented-ui is a css kit you can include via cdn or npm pkg that transforms your site into cyberpunk: https://medium.com/better-programming/augmented-ui-cyberpunk-inspired-web-ui-made-easy-d463c0371144.
    • Foam roll on the legs = crucial for training.
    • Awesome website that does the same thing Ken’s script used to do: https://www.fantasy-football-power-rankings.com/espn/1187979/2019. Power rankings, where it lists your record if you were to play every other team every week. Just like total points for, it is a more absolute metric of success without the random luck of head-head matchup order. Yahoo already reports this in your weekly recap, but ESPN doesn’t, so this site provides it. https://github.com/JakePartusch/fantasy-football-power-rankings.
    • If joining the same table twice for multiple conditions, use sqlalchemy.orm.aliased. Same as a simple table table2 rename in sql.
    • Gonna feed gbro 1/2c twice a day instead of leaving big bowls out to self feed. This is slightly more than the recommended amount, but he’s definitely overweight. I want to see how much he’s consuming, then briley can adjust as he wants.
    • If you create a list in python with [x] * n, then they all point to the same object. If you update one of them, every element will mirror the same update.
    • Made another batch of tahini, but instead of using 100% sesame seeds, I used a ratio of about 80% sesame to 20% pecans, yielding pecan tahini. Incredible.
    • Made another batch of protein bars: oats, dates, protein powder, pecan tahini, banana, cinnamon, cacao powder. Incredible.
    • Read through most of the graphql documentation again.
      • Remember, it converts resolver names and such from snake_case to camelCase. I disably this during schema instantiation with auto_camelcase=False.
      • Within an ObjectType class, each resolve_x method corresponding to the field x that you define in the constructor.
      • They intentionally do not allow select *. Graphql is meant to be deliberate: fetch only what you need, not all cols. https://github.com/graphql/graphql-spec/issues/127.
      • SQLAlchemyConnectionField is the magic liason. Should map most of your entire model, all cols, field types, sortability, etc.
      • There is an extension to this that allows filtering: https://pypi.org/project/graphene-sqlalchemy-filter/. graphene-sqlalchemy can’t do it right out of the box (simply). You’d have to add custom resolvers that pass args through query.filter() etc.
      • It’s worth mentioning: the documentation surrounding this effort is horrendous. I have a very simple use case: take a few sqlalchemy models with foreign keys, allow flask graphql queries with filters on cols, for both direct queries as well as queries on joined tables. This common implementation should be trivial to implement, and it’s not even close to that.
      • Filters: eq, ne, like, ilike, regexp, is_null, in, not_in, lt, lte, gt, gte, range.
        • Example: {seasons(filters:{season_start_lt:”2019-06-01″}){edges{node{id season}}}}. This would return just the rows from 2018 (lt = less than), and only the id and year number.
      • Datetimes are of the form “2019-11-01T00:00:00”
    • sqlalchemy has an `add_columns` which you can call after a join to create a more sql-standard select return table. https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.add_columns. This can be used to get around the necessity of unpacking the multiple table results if you query a join. You can even add labels.
    • TSS stands for Transportation Software Solutions. Let Stacey, Roubik, and Art know that my intentions are to move back to the Bay Area.
    • Remy and Katilin got sick on Monday too! It wasn’t the prime rib, because everyone had that and only 3 of us reacted. I’ve also had it a few times since with no problem. Possibilities: dip, cheese, gravy? The latenight snack after stuff had been sitting out could have been it.
    • There’s a paradox about predictability; that once something becomes predictable, entropy naturally makes it move away toward unpredictable again. I half-buy it. I’m a believer in determinism superseding free will in the longggg future.
    • Your body can actually burn an incredible number of calories while sedentary (comparable to rigorous physical exercise), if you are highly stressed/concentrated: https://www.espn.com/espn/story/_/id/27593253/why-grandmasters-magnus-carlsen-fabiano-caruana-lose-weight-playing-chess.
    • Agreed. Personal websites are wonderful tools. https://www.vanschneider.com/a-love-letter-to-personal-websites.
    • GitLab is a company I had seriously considered about 2 years ago: https://www.forbes.com/sites/alexkonrad/2019/09/17/gitlab-doubles-valuation-to-nearly-3-billion. They’re doing very well, and my professional experience matches their mission absurdly well. Their IPO is in 1 year.
    • Chrome’s devtools has a “device toolbar” that you can toggle on and off by clicking the little button that looks like a phone. You can specify any custom viewport, standard phones, and it will show your how your responsive view is displayed. Great for testing.
    • Hahaha AB released from the Pats. What a roller coaster.
    • Supercontest.
      • Added mx-auto to all nav cols so that they’d center on smaller viewports, tested in devtools. Looks much better.
      • The lb totals and percentages will now show as ints if they’re whole numbers (even if source data is X.0). If they’re floats, they’ll continue to show as floats.
      • Webassets.
        • Moved all third-party cdn js to the toplevel template.
        • Moved all variables (that were passed through jinja from flask to javascript) to the toplevel template.
        • Bundled and minified all supercontest js, added in the toplevel template.
        • Cleaned some of the iteration in jinja, setting variables properly.
        • At this point, all templates are just templates. It’s a lot cleaner. All variable injection is collocated, which is easier to understand as well.
        • Made all js in the bundle distinct. Some required more specific ID selectors. Some needed to check on array length. Some needed to condition on typeof <x> !== undefined. This whole exercise was a good practice in forcing explicit javascript.
        • Minifying with jsmin.
      • Package the service.
        • I don’t just want to copy an ambiguous directory over to be able to use the app. It should have explicit boundaries. This will keep it slim, also.
        • Therefore, I made the service a self-sufficient package. It can’t run without an accompanying database already existing, but the app layer itself is self-sufficient.
        • This was a pretty lengthy, iterative process to get to work.
        • MANIFEST only tells it what to build the dist with; not necessarily what to install. You have to be explicit about those as data files in setup.py.
      • Overall, working with bundled assets and wheel pkgs should make the app non-noticeably faster.
      • Closed https://github.com/brianmahlstedt/supercontest/issues/81.
    • Docker.
      • If you built an image successfully but it failed to run the start command or entrypoint for some reason or another, perform the following to test.
        • docker ps -a
        • docker commit <ID> test_image
        • docker run -ti –entrypoint=sh test_image
      • To force a rebuild, even if nothing has changed:
        • docker-compose build –no-cache <service>
        • Then you can start the service at will.
    • tar -xzvf (I can never remember anyway – this is a worthless comment).
    • Remember to build wheels in the same environment that you’ll install them in. Not the same instance, but the same system. There are many underlying system libs that are baked into bdist wheels, and the build/deploy envs should be identical to prevent clashes for this.
    • find_packages() returns a list like this: [‘supercontest’, ‘supercontest.core’, ‘supercontest.views’, ‘supercontest.dbsession’, ‘supercontest.commands’, ‘supercontest.models’, ‘supercontest.graphql’]
    • Supercontest.
      • Datetime, picks, leaderboard:
        • Updated all the datetimes in the database to pacific. Updated the database timezone to pacific.
        • Updated the url defaults. Season is current or max. Week is current or 17.
        • Added g.current_season next to the existing g.season (which is the requested season) and g.is_current_season. Same for weeks.
        • Switched everything to be perfectly clear: all datetimes in the database are utc, all westgate lines are written after conversion from pacific time, and all sqlalchemy reads convert to local.
        • Debug toolbar is helping a lot. Noticed that on RESULTS_DAYS, it was bloated by about 500ms due to excessive queries.score_exists/get_score calls. Consolidated all of them. You could even go further by conditioning commit_scores not just on RESULTS_DAYS, but on time as well to tighten the range in which the extra calls are made. Call fetch_scores protected by results_days, then only call commit if any of the statuses aren’t P/F/FO.
          • Reduced total db queries for the endpoint by >30 and profile reduced by ~400ms.
        • Completed redefined the pick view and how it’s sorted, including the core/results backend to calculate as such. Now returns number of picks, current total points, current possible points, etc. Looks very good now.
        • LB and graph only show completed games in the total now. I added a col for in-progress games so you call still check “what if it all ended right now” status.
        • Percentages are now based on the picks that you have submitted, not the total (5 per week).
        • Combined a lot of the route logic, keeping the endpoints as thin as possible. Most of them outsource to functions in the core.results module now.
        • Added tooltips all over leaderboard for submitted picks, help, more.
        • Universal color for a lot of things.
        • Renamed the matchups tab to your picks. Renamed the picks tab to all picks.
        • Graph and leaderboard are now bulletproof for completed games.
        • Added req compilation back as a requirement of the test step, and ensured that compreqs were installed into the testenv.
      • My make test-python recipe is heavy-handed; it will compile all reqs and recreate the testenv. On subsequent runs, just use tox.
      • Got the bandit tests running again. Pytest is still obsolete.
      • App behaved well over TNF.
      • Closed https://github.com/brianmahlstedt/supercontest/issues/84.
      • Closed https://github.com/brianmahlstedt/supercontest/issues/94.
      • Dashboard.
        • Added a conditional (on my account) navtab for the flask monitor.
    • Bools have to be filtered |tojson to pass through jinja from flask to js. They’ll be properly typeof() = boolean in js.
    • Forcing NBA owners to change their titles to governors due to historical implications is the same false equivalence as telling a German that they can’t use ovens. It’s offensive. It’s irrelevant. Race is being used as glue to compare situations which have absolutely no relation.  It’s not just a “doesn’t affect you” situation – it’s discriminatory, and should not be ok to concede. Finding these parallels with your lens of virtuous sensitivity is not progressive; it’s an overshot of true equality, which slows resolution (if it doesn’t destabilize entirely).
    • Nested dicts are fine to pass through jinja’s |tojson filter for use in js.
    • Fed mouse. First time she’s ever been snappy when I opened the terrarium. Used a broomstick to move her to the feeding tank.
    • My digital ocean droplet is in Santa Clara.
    • Supercontest.
    • Followup appointment with PCP. Got the referrals approved for both ortho and for PT.
      • The x-ray results:
        • Soft tissues: Unremarkable.
        • Bones: No acute fracture or subluxation. No lytic or blastic lesion.
        • Joints: The joint spaces are preserved. Articular surfaces are unremarkable.
      • So x-ray is fine. I’ll probably need to do an MRI, since this would imply that the muscle is the source of injury.
      • Called the ortho place, made an appointment in Van Nuys for next wednesday.
      • I’m going to hold off on scheduling the PT until after the ortho evaluation is done. Stim, stretching, strengthening, etc – I can do that all on my own. The referral won’t expired for 3 months, so I can schedule PT after ortho if need be.
    • Got adjusted at chiro (bought the 3/$90), then went to the new Chick-Fil-A.
    • For the last time: snake_case and camelCase and PascalCase.
    • Reminder for ISO8601: https://www.cl.cam.ac.uk/~mgk25/iso-time.html. All dates should be in the format YYYY-MM-DD.
    • A pipe | in a makefile is for order-only prerequisites. If you change any of those files, the parent recipe does not deem them necessary to rerun.
    • Postgres.
      • To get datatypes:
        • select column_name, data_type from information_schema.columns where table_name = ‘<mytable>’;
        • select pg_typeof(<mycol>) from <mytable> limit 1;
      • I was very pleasantly surprised with postgresql’s ability to internally parse timestamps. It correctly converted the type ‘text’ to ‘timestamp with time zone’ for every single datetime string from westgate. I thought I would have to use the python logic I had written to do this, but it was dead easy using sql in the migration instead.
      • lag() is a window function that allows you to update a row based on a previous row.
    • You can use direct sqlalchemy inside of an alembic migration if you want. This might be easier to (add, modify, etc) your data in python rather than doing it in sql. https://stackoverflow.com/questions/13676744/using-the-sqlalchemy-orm-inside-an-alembic-migration-how-do-i/
    • KFC is rolling out a chicken sandwich that uses glazed donuts instead of buns: https://www.cnn.com/2019/09/17/us/kfc-donut-chicken-sandwich-trnd/index.html.
    • Remember https://www.pngtosvg.com/ is the right place for free online vector conversion with color.
    • Just print(locals()) to show all args/kwargs passed to a function.
    • Changed my default text editor for git to vim: git config –global core.editor “vim”
    • Rob Zombie was on JRE a couple days ago. Cool dude. Just directed another movie, 3 from Hell.
    • Stomach was torn up all yesterday, couldn’t do much. Worse than the ER trip in the bay, but stuck it out. Didn’t sleep, lasted about 24 hrs, but feel better now. Ordered imodium, pedialyte, and pepto bismol on amazon fresh and it arrived within 8 hrs!
    • Lost both FF games. Went 1/5 in supercontest.
    • Supercontest.
      • Went through my giant migration and verified it with pure sql incrementally. Made sure the season/week change were good, user_season_association, then on to picks and scores etc. Made it a lot easier to chunk on the pure db side rather than debugging through the application layer.
      • The problem ended up being the FKs back to week and season for the lines and picks tables. They were matching those two numbers, but not joining lines/weeks/seasons on their id conditions so lines and picks were going to multiple places. Simply added a couple join conditions to restrict this properly.
      • Removed some of the smaller tasks of 68 to a followup ticket 86: https://github.com/brianmahlstedt/supercontest/issues/86.
      • Since the leaderboard iterates over picks, it only shows players who have made picks at all that season. The same is now true for the picks tab.
      • Closed an old ticket: https://github.com/brianmahlstedt/supercontest/issues/33.
      • User registration defaults to add you to the most recent season, but we’ll need a way to do this efficiently at each season rollover. I guess for the 2020-2021 season, I’ll just run quick sql to add everyone who was in 2019-2020 over, then we can prune after week 2-3 like usual.
      • Made determine_current_pick_points() modular to not just require a Pick row. Now, you can provide a Matchup row (line/scores) and a team string, simulating a pick.
      • Picks can be made by any user at any time, no matter if they’re “active” for that year or not. That’s just for our records, and requires the script be run to sync that data based on the existence of picks that year.
      • Finished and closed all the gigantic model changes. https://github.com/brianmahlstedt/supercontest/issues/68. On to the fun stuff.
      • Fixed datetime and improved the lb: https://github.com/brianmahlstedt/supercontest/issues/86.
      • Added a request tracking dashboard: https://github.com/brianmahlstedt/supercontest/issues/77. Useful for timing and profiling over time, and by version. This will be used for production.
      • Added a debug toolbar: https://github.com/brianmahlstedt/supercontest/issues/78. This does a huge superset of the dashboard. You can see sqlalchemy queries, http headers, profiling, total times, etc; a plethora of information for a single request (every time).
      • Made the all-picks view show the team name abbreviations throughout the table so that when you scroll down you’re not lost. Added tooltips for the lines as well. https://github.com/brianmahlstedt/supercontest/issues/83.
    • lol: https://www.youtube.com/watch?v=4sqYEmAy9Dg.
    • “If you go to jail for tax evasion, you’re living off of taxes as a result of not paying taxes.” – Joe Rogan
    • For more complicated boolean queries in sqlalchemy, use not_ and_ etc.
    • Remember, with csrf enabled globally for an app, a lot of plugins might get tangled. You can specifically call csrf.exempt(view_or_blueprint), but this often means going into the source code for an extension to find out how to import the view/blueprint for exemption.
    • For rgb colors, if all 3 numbers match, then it will produce a color in grayscale. All zeros is black. all 255s is white.