Mercurial > kallithea
view kallithea/controllers/journal.py @ 6230:cd6176c0634a
db: PullRequest/Repository/RepoGroup/UserGroup: change 'user' to 'owner'
Rename the 'user' and 'user_id' fields on the four classes to something
more informative. The database column names remain unchanged for now;
a later Alembic script can fix the name of these and other columns to
match their Python name.
This might break rcextensions, though, and external scripts that use
the HTML form interface.
author | Søren Løvborg <sorenl@unity3d.com> |
---|---|
date | Wed, 14 Sep 2016 16:24:05 +0200 |
parents | 873a3839865d |
children | e99a33d7d7f5 |
line wrap: on
line source
# -*- coding: utf-8 -*- # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ kallithea.controllers.journal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Journal controller This file was forked by the Kallithea project in July 2014. Original author and date, and relevant copyright and licensing information is below: :created_on: Nov 21, 2010 :author: marcink :copyright: (c) 2013 RhodeCode GmbH, and others. :license: GPLv3, see LICENSE.md for more details. """ import logging import traceback from itertools import groupby from sqlalchemy import or_ from sqlalchemy.orm import joinedload from sqlalchemy.sql.expression import func from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed from webob.exc import HTTPBadRequest from pylons import request, tmpl_context as c, response from pylons.i18n.translation import _ from kallithea.config.routing import url from kallithea.controllers.admin.admin import _journal_filter from kallithea.model.db import UserLog, UserFollowing, Repository, User from kallithea.model.meta import Session from kallithea.model.repo import RepoModel import kallithea.lib.helpers as h from kallithea.lib.helpers import Page from kallithea.lib.auth import LoginRequired, NotAnonymous from kallithea.lib.base import BaseController, render from kallithea.lib.utils2 import safe_int, AttributeDict from kallithea.lib.compat import json log = logging.getLogger(__name__) class JournalController(BaseController): def __before__(self): super(JournalController, self).__before__() self.language = 'en-us' self.ttl = "5" self.feed_nr = 20 c.search_term = request.GET.get('filter') def _get_daily_aggregate(self, journal): groups = [] for k, g in groupby(journal, lambda x: x.action_as_day): user_group = [] #groupby username if it's a present value, else fallback to journal username for _unused, g2 in groupby(list(g), lambda x: x.user.username if x.user else x.username): l = list(g2) user_group.append((l[0].user, l)) groups.append((k, user_group,)) return groups def _get_journal_data(self, following_repos): repo_ids = [x.follows_repository.repo_id for x in following_repos if x.follows_repository is not None] user_ids = [x.follows_user.user_id for x in following_repos if x.follows_user is not None] filtering_criterion = None if repo_ids and user_ids: filtering_criterion = or_(UserLog.repository_id.in_(repo_ids), UserLog.user_id.in_(user_ids)) if repo_ids and not user_ids: filtering_criterion = UserLog.repository_id.in_(repo_ids) if not repo_ids and user_ids: filtering_criterion = UserLog.user_id.in_(user_ids) if filtering_criterion is not None: journal = self.sa.query(UserLog) \ .options(joinedload(UserLog.user)) \ .options(joinedload(UserLog.repository)) #filter journal = _journal_filter(journal, c.search_term) journal = journal.filter(filtering_criterion) \ .order_by(UserLog.action_date.desc()) else: journal = [] return journal def _atom_feed(self, repos, public=True): journal = self._get_journal_data(repos) if public: _link = h.canonical_url('public_journal_atom') _desc = '%s %s %s' % (c.site_name, _('Public Journal'), 'atom feed') else: _link = h.canonical_url('journal_atom') _desc = '%s %s %s' % (c.site_name, _('Journal'), 'atom feed') feed = Atom1Feed(title=_desc, link=_link, description=_desc, language=self.language, ttl=self.ttl) for entry in journal[:self.feed_nr]: user = entry.user if user is None: #fix deleted users user = AttributeDict({'short_contact': entry.username, 'email': '', 'full_contact': ''}) action, action_extra, ico = h.action_parser(entry, feed=True) title = "%s - %s %s" % (user.short_contact, action(), entry.repository.repo_name) desc = action_extra() _url = None if entry.repository is not None: _url = h.canonical_url('changelog_home', repo_name=entry.repository.repo_name) feed.add_item(title=title, pubdate=entry.action_date, link=_url or h.canonical_url(''), author_email=user.email, author_name=user.full_contact, description=desc) response.content_type = feed.mime_type return feed.writeString('utf-8') def _rss_feed(self, repos, public=True): journal = self._get_journal_data(repos) if public: _link = h.canonical_url('public_journal_atom') _desc = '%s %s %s' % (c.site_name, _('Public Journal'), 'rss feed') else: _link = h.canonical_url('journal_atom') _desc = '%s %s %s' % (c.site_name, _('Journal'), 'rss feed') feed = Rss201rev2Feed(title=_desc, link=_link, description=_desc, language=self.language, ttl=self.ttl) for entry in journal[:self.feed_nr]: user = entry.user if user is None: #fix deleted users user = AttributeDict({'short_contact': entry.username, 'email': '', 'full_contact': ''}) action, action_extra, ico = h.action_parser(entry, feed=True) title = "%s - %s %s" % (user.short_contact, action(), entry.repository.repo_name) desc = action_extra() _url = None if entry.repository is not None: _url = h.canonical_url('changelog_home', repo_name=entry.repository.repo_name) feed.add_item(title=title, pubdate=entry.action_date, link=_url or h.canonical_url(''), author_email=user.email, author_name=user.full_contact, description=desc) response.content_type = feed.mime_type return feed.writeString('utf-8') @LoginRequired() @NotAnonymous() def index(self): # Return a rendered template p = safe_int(request.GET.get('page'), 1) c.user = User.get(self.authuser.user_id) c.following = self.sa.query(UserFollowing) \ .filter(UserFollowing.user_id == self.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() journal = self._get_journal_data(c.following) def url_generator(**kw): return url.current(filter=c.search_term, **kw) c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator) c.journal_day_aggregate = self._get_daily_aggregate(c.journal_pager) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('journal/journal_data.html') repos_list = Repository.query(sorted=True) \ .filter_by(owner_id=self.authuser.user_id).all() repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list, admin=True) #json used to render the grid c.data = json.dumps(repos_data) watched_repos_data = [] ## watched repos _render = RepoModel._render_datatable def quick_menu(repo_name): return _render('quick_menu', repo_name) def repo_lnk(name, rtype, rstate, private, fork_of): return _render('repo_name', name, rtype, rstate, private, fork_of, short_name=False, admin=False) def last_rev(repo_name, cs_cache): return _render('revision', repo_name, cs_cache.get('revision'), cs_cache.get('raw_id'), cs_cache.get('author'), cs_cache.get('message')) def desc(desc): from pylons import tmpl_context as c return h.urlify_text(desc, truncate=60, stylize=c.visual.stylify_metatags) def repo_actions(repo_name): return _render('repo_actions', repo_name) def owner_actions(user_id, username): return _render('user_name', user_id, username) def toogle_follow(repo_id): return _render('toggle_follow', repo_id) for entry in c.following: repo = entry.follows_repository cs_cache = repo.changeset_cache row = { "menu": quick_menu(repo.repo_name), "raw_name": repo.repo_name, "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state, repo.private, repo.fork), "last_changeset": last_rev(repo.repo_name, cs_cache), "last_rev_raw": cs_cache.get('revision'), "action": toogle_follow(repo.repo_id) } watched_repos_data.append(row) c.watched_data = json.dumps({ "totalRecords": len(c.following), "startIndex": 0, "sort": "name", "dir": "asc", "records": watched_repos_data }) return render('journal/journal.html') @LoginRequired(api_access=True) @NotAnonymous() def journal_atom(self): """ Produce an atom-1.0 feed via feedgenerator module """ following = self.sa.query(UserFollowing) \ .filter(UserFollowing.user_id == self.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() return self._atom_feed(following, public=False) @LoginRequired(api_access=True) @NotAnonymous() def journal_rss(self): """ Produce an rss feed via feedgenerator module """ following = self.sa.query(UserFollowing) \ .filter(UserFollowing.user_id == self.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() return self._rss_feed(following, public=False) @LoginRequired() @NotAnonymous() def toggle_following(self): user_id = request.POST.get('follows_user_id') if user_id: try: self.scm_model.toggle_following_user(user_id, self.authuser.user_id) Session.commit() return 'ok' except Exception: log.error(traceback.format_exc()) raise HTTPBadRequest() repo_id = request.POST.get('follows_repo_id') if repo_id: try: self.scm_model.toggle_following_repo(repo_id, self.authuser.user_id) Session.commit() return 'ok' except Exception: log.error(traceback.format_exc()) raise HTTPBadRequest() raise HTTPBadRequest() @LoginRequired() def public_journal(self): # Return a rendered template p = safe_int(request.GET.get('page'), 1) c.following = self.sa.query(UserFollowing) \ .filter(UserFollowing.user_id == self.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() journal = self._get_journal_data(c.following) c.journal_pager = Page(journal, page=p, items_per_page=20) c.journal_day_aggregate = self._get_daily_aggregate(c.journal_pager) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('journal/journal_data.html') return render('journal/public_journal.html') @LoginRequired(api_access=True) def public_journal_atom(self): """ Produce an atom-1.0 feed via feedgenerator module """ c.following = self.sa.query(UserFollowing) \ .filter(UserFollowing.user_id == self.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() return self._atom_feed(c.following) @LoginRequired(api_access=True) def public_journal_rss(self): """ Produce an rss2 feed via feedgenerator module """ c.following = self.sa.query(UserFollowing) \ .filter(UserFollowing.user_id == self.authuser.user_id) \ .options(joinedload(UserFollowing.follows_repository)) \ .all() return self._rss_feed(c.following)