Metadata-Version: 2.4
Name: company-portfolio-package
Version: 0.1.0
Summary: Company/group site package for the Core boilerplate: a company profile plus members with public pages and downloadable (uploaded or auto-generated PDF) CVs.
Author-email: puzzllium <puzzllium@gmail.com>
Maintainer-email: puzzllium <puzzllium@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/puzzelchannel/company-package
Project-URL: Issues, https://github.com/puzzelchannel/company-package/issues
Keywords: django,company,team,members,cv,django-ninja
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: core-package>=0.2.0
Requires-Dist: portfolio-package>=0.1.0
Requires-Dist: blog-package>=0.1.1
Requires-Dist: django-ninja>=1.7.0
Requires-Dist: django-unfold>=0.81.0
Requires-Dist: Pillow>=10.0
Requires-Dist: reportlab>=4.0
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-django>=4.14; extra == "dev"
Requires-Dist: pytest-asyncio>=1.4; extra == "dev"
Dynamic: license-file

# company-portfolio-package

A company/group site package for the [Core](https://pypi.org/project/core-package/) boilerplate: a 
single `Company` profile (name, tagline, description, logo, contact info)
plus its `Member`s — each member is a Core `User` with their own public page
(job title, department, tenure) whose bio/headline/skills/education/
certifications/social links come from the `portfolio` package, not a second
copy of the same content here. It depends on `core-package` for
auth, RBAC, and the shared `User` model, and on `portfolio-package` for that
profile content — **it never defines its own user/account model or
duplicates portfolio's fields.**

This package models **one** company/group (a singleton `Company` row,
created automatically on first read — see `services.get_company()`), not a
multi-tenant directory of many companies. If you need the latter, this
package's `Member` model (drop its implicit "the one company" assumption and
add a `company` FK) is the natural starting point, not a rewrite.

`Company.tagline`/`description` and `Member.title` are translatable via
`apps.core.models.TranslatableModel` — a single `translations` JSON field
per row, not a `tagline_en`/`tagline_fa` column per language. Add a language
by calling `PUT /translations/{language}` or
`PUT /members/{id}/translations/{language}`, no migration required.

## Downloadable CVs

Both the company/group and each member expose `GET .../cv`:

- If an admin (or, for a member, whoever has upload rights) has uploaded a
  CV file (`Company.cv_file` / `Member.cv_file`), it's served as-is.
- Otherwise a CV is generated on the fly as a PDF (`company/pdf.py`, via
  `reportlab`) from the structured data already on hand: for a member, their
  `Member` fields plus their `portfolio` `Profile`/`Skill`/`Education`/
  `Certification`/`SocialLink` rows; for the company, the `Company` row plus
  a directory of its active members. This always succeeds — there's no
  "no CV available" state — and stays in sync with profile edits without
  anyone re-uploading a file.

`reportlab` was chosen specifically because it's pure Python (no system
libraries like Pango/Cairo that e.g. WeasyPrint needs), so this package
installs the same way on every machine.

See Core's `AGENT.md` §10 ("Package architecture") for the full contract this
package follows.

## Install

```bash
pip install company-portfolio-package
```

Requires Python 3.10+ and Django 5.2 (via core-package). This also installs
`portfolio-package` and `blog-package`.

## Local development

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest
```

## Installing into a Core-based project

1. Install it into the host project's venv:
   ```bash
   pip install company-portfolio-package
   ```
   This also pulls in `core-package`, `portfolio-package` and, transitively,
   `blog-package` automatically. You still need to do
   steps 2-4 for `company` itself (and, if not already done, for `portfolio`/
   `blog` too) — Core's "no auto-discovery, explicit wiring" convention
   (§10) means the pip install is automatic, but `INSTALLED_APPS`/
   router-mounting/`migrate` are not.
2. Add it to `INSTALLED_APPS` in the host's `config/settings/base.py`, under
   the `# site packages` comment (after `portfolio`, since this package
   imports `portfolio.models` directly):
   ```python
   # site packages
   "blog",
   "portfolio",
   "company",
   ```
3. Mount its router in the host's `config/api.py`:
   ```python
   from company.api import router as company_router
   ...
   api.add_router("/company", company_router)
   ```
4. Migrate:
   ```bash
   python manage.py migrate
   ```
5. Optional — add a nav entry under `UNFOLD["SIDEBAR"]["navigation"]` in the
   host's `config/settings/base.py` for `Company`/`Member`.

## Endpoints

Mounted at whatever prefix the host chooses (`/company` in the example
above). Reads are public; all writes are admin-only
(`require_role("admin")`, from Core).

- `GET /` — the company/group profile (public), optional `?lang=`
- `GET /content` — the whole landing-page document (public): brand, hero,
  about, services, projects, steps, stats, team, contact, socials, clients,
  testimonial, FAQ, values, timeline, posts, categories. Every text is
  `{fa, en}` (languages from `COMPANY_CONTENT_LANGUAGES`, default `fa`+`en`);
  services/projects come from `portfolio`, posts from `blog`, team from
  `Member`. Point the Puzzllium site's `NEXT_PUBLIC_API_URL` at `.../company`.
- `GET /content` is public unless `COMPANY_CONTENT_API_KEY` is set in the
  host's settings; then it needs an `X-API-Key: <key>` header (401 otherwise).
- `GET /cv` — download the company/group CV (public): the uploaded file if
  one exists, else an auto-generated PDF
- `GET /members` — paginated list of active members (public), optional
  `?lang=`
- `GET /members/{member_id}` — a member's public page (public), optional
  `?lang=` — merges `Member` with their `portfolio` `Profile`
- `GET /members/{member_id}/cv` — download that member's CV (public): the
  uploaded file if one exists, else an auto-generated PDF
- `PATCH /`, `PUT /translations/{language}` — update the company profile /
  its translations
- `POST /logo`, `POST /cv` — upload the company logo / CV file (multipart)
- `POST /members`, `PATCH /members/{member_id}`,
  `DELETE /members/{member_id}` — manage membership (links an existing
  `user_id`; does not create or delete the `User` itself)
- `PUT /members/{member_id}/translations/{language}` — set a member's
  translated `title`
- `POST /members/{member_id}/cv` — upload a member's CV file (multipart)

## Seeding the landing-page content

```bash
python manage.py import_site_content puzzllium-content.json --owner-email you@example.com
```

Loads a site-content JSON into `Company`, the content blocks (steps, stats,
values, timeline, FAQ, categories, socials, testimonial) and the owner's
`portfolio` services/projects and `blog` posts (English in the base columns,
other languages in `translations`). Team members are real accounts and are
not created — add them as `Member`s (with a `name`) and fill their portfolio
`Profile`/skills. Images are uploaded in the admin. Requires `blog` in
`INSTALLED_APPS` next to `portfolio` and `company`.
