The Architecture ================ Here we give a deeper dive into how things are implemented (beyond making a simple problem). We first address the question, how are problems actually imported into the website? If you are coming from "Adding a Simple Problem" you might be surprised at how little-to-no boilerplate is needed when making a score function. We use some import magic for *both* the frontend verification and backend verification. -------------------------------------- Backend Verification: Python Importing -------------------------------------- A problem is registered using the file ``conjecscore/app/routers/problems/problems.py``. In particular, the files for the problems are found using `Line 179 `_ by iterating through each ``.json`` file in the ``registry`` file. The ``register_problem`` takes the problem details from the ``registry`` and converts that information into a route. ------------------------------------------- Frontend Verification: Typescript Importing ------------------------------------------- It is a little bit more complicated for frontend. Files are found *not* via Typescript but via Python again in ``conjecscore/app/routers/problems/problems.py``. The files that are found are then injected into the HTML via Jinja. In particular, the Typescript files and functions are injected into ``conjecscore/templates/make-problem.j2``. ``make-problem.j2`` creates a ``Problem`` object (see ``conjecscore/static/problem.ts`` at `Line 65 `_) containing the (frontend) score function, the url to post scores to, what kind of input to use (does the problem take a single number, JSON, CSV, etc.), and any information on problem variants. In addition to storing problem information in a ``Problem`` object, ``make-problem.j2`` also creates event listeners for the particular problem (that way, the problem creator does not need to worry about this!) In conjunction with ``make-problem.j2`` The HTML input forms are generated via ``conjecscore/templates/submission-form.j2``. Lastly, ``problem_template.j2`` combines various templates including ``make-problem.j2``, ``submission-form.j2``, and the correct template associated with a particular problem to actually render a functional problem page. ------------- Other systems ------------- We have now discussed how a problem gets rendered to a page, the webpage also consists of a couple other major components. Namely, a problem database, a login/registration system, and a user page. -------------------- The Problem Database -------------------- There are two database schemas that are used in conjecscore: A ``User`` table for managing account information, and an ``Entry`` table for managing submissions made by users. Both can be found in ``conjecscore/app/db.py`` at `Line 17 `_ and `Line 22 `_, respectively. The ``User`` schema implicitly has some fields generated by `FastAPI-Users `_ but it consists of an email as the *primary* key (which is not visible to other users). A password (which, obviously is not exposed). And a ``nickname`` which is displayed in various places such as the scoreboards, user profile, and the ``/users`` route. The ``Entry`` schema is a little bit more complicated. An ``Entry`` corresponds to a submission made by a user. Since memory is limited *only the best scoring entry for a particular user is kept.* Hence, we enforce that ``account_id`` (the ID for the account that submitted the entry) is unique. We also store the other account information along with the entry (such as the nick name). Lastly, we keep some obvious fields: ``score`` the score the submission achieved. ``problem`` the particular problem (Collatz, Brocard, etc.) that the submission is associated with. ``variant`` if the problem has variants then there must be separate submissions for each variant. ---------------------- Login and Registration ---------------------- We have mentioned the ``User`` entry in the previous section which *stores* the user information. Other major files for Login and Registration include: - ``conjecscore/templates/login.j2`` and ``conjecscore/templates/newaccount.j2``: The Jinja2 files that generate the HTML for login and register pages respectively. (The routes are served by FastAPI in ``conjecscore/app/main.py``). - ``conjecscore/static/login.ts`` and ``conjecscore/static/register.ts`` the Typescript for the login and register page. For the future, work will need to be done to add OAuth. .. note:: Once a user is logged in they can visit their profile page at ``/me``. Let us look at the route function signature for the ``/me`` route on `Line 115 `_: .. code-block:: python async def me(request: Request, user: User=Depends(current_active_user)): The ``request`` is fairly standard among all routes. A request is *always* made to a route. ``user: User=Depends(current_active_user)`` checks to see if the user is actually logged in. If not ``user is None``. Otherwise, there is a user session. (This is largely handled by the FastAPI-Users library. ----------------------------------- The User Page: A More In Depth Look ----------------------------------- Going back to ``/me`` route, we have a fairly complicated route function, but at a highlevel: - We get (from the ``Entry`` table) each problem submission for the logged in user. - Render these entries via ``conjecscore/templates/profile.j2``. - Get the best entries for each problem. - Compute the overall score (and also render with ``profile.j2``). (See below for how overall score is computed.) What makes the function so long is computing the *overall score*. The function is roughly the sum of: ``max(0, 100 + 200 * (user_score / best_score))`` for each problem. It just turns out there are some annoying issues when the user has not actually submitted a problem (or there are no submissions at all). This scoring method ensures you get ``100`` points for attempting a problem (``0`` otherwise) and can achieve a maximum of ``300`` points if they have the highest score on the leaderboard. ----------------------- A Couple of Other Pages ----------------------- We have covered the most complicated pages, and have just left ``/problems`` and ``/users``. Fortunately, most of the heavy-lifting for these pages is just a simple Jinja for-loop. For ``/problems`` this loop is located in ``conjecscore/templates/problems.j2`` which renders a little card (generated by the ``conjecscore/templates/problem-card.j2`` template) for each problem. And for ``/users`` this loop is located in ``conjecscore/templates/users.j2`` which just lists every user nickname.