diff --git a/modules/webbasket/lib/webbasket_dblayer.py b/modules/webbasket/lib/webbasket_dblayer.py index c26f2921e..6d3157919 100644 --- a/modules/webbasket/lib/webbasket_dblayer.py +++ b/modules/webbasket/lib/webbasket_dblayer.py @@ -1,2300 +1,2296 @@ # -*- coding: utf-8 -*- ## ## This file is part of Invenio. ## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. ## ## Invenio 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 2 of the ## License, or (at your option) any later version. ## ## Invenio 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 Invenio; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. """ Database related functions for webbasket module """ __revision__ = "$Id$" from zlib import decompress from zlib import compress from time import localtime from invenio.textutils import encode_for_xml from invenio.dbquery import run_sql from invenio.webcomment import get_reply_order_cache_data from invenio.webbasket_config import CFG_WEBBASKET_SHARE_LEVELS, \ CFG_WEBBASKET_ACTIONS, \ CFG_WEBBASKET_SHARE_LEVELS_ORDERED, \ CFG_WEBBASKET_MAX_COMMENT_THREAD_DEPTH from invenio.dateutils import convert_datestruct_to_datetext from invenio.websession_config import CFG_WEBSESSION_USERGROUP_STATUS ########################### Table of contents ################################ # # NB. functions preceeded by a star use usergroup table # # 1. General functions # - count_baskets # - check_user_owns_basket # - get_max_user_rights_on_basket # # 2. Personal baskets # - get_personal_baskets_info_for_topic # - get_all_personal_basket_ids_and_names_by_topic # - get_all_personal_baskets_names # - get_basket_name # - is_personal_basket_valid # - is_topic_valid # - get_basket_topic # - get_personal_topics_infos # - rename_basket # - rename_topic # - move_baskets_to_topic # - delete_basket # - create_basket # # 3. Actions on baskets # - get_basket_record # - get_basket_content # - get_basket_item # - share_basket_with_group # - update_rights # - move_item # - delete_item # - add_to_basket # - get_external_records_by_collection # - store_external_records # - store_external_urls # - store_external_source # - get_external_colid_and_url # # 4. Group baskets # - get_group_basket_infos # - get_group_name # - get_all_group_basket_ids_and_names_by_group # - (*) get_all_group_baskets_names # - is_shared_to # # 5. External baskets (baskets user has subscribed to) # - get_external_baskets_infos # - get_external_basket_info # - get_all_external_basket_ids_and_names # - count_external_baskets # - get_all_external_baskets_names # # 6. Public baskets (interface to subscribe to baskets) # - get_public_basket_infos # - get_public_basket_info # - get_basket_general_infos # - get_basket_owner_id # - count_public_baskets # - get_public_baskets_list # - is_basket_public # - subscribe # - unsubscribe # - is_user_subscribed_to_basket # - count_subscribers # - (*) get_groups_subscribing_to_basket # - get_rights_on_public_basket # # 7. Annotating # - get_notes # - get_note # - save_note # - delete_note # - note_belongs_to_item_in_basket_p # # 8. Usergroup functions # - (*) get_group_infos # - count_groups_user_member_of # - (*) get_groups_user_member_of # # 9. auxilliary functions # - __wash_sql_count # - __decompress_last # - create_pseudo_record # - prettify_url ########################## General functions ################################## def count_baskets(uid): """Return (nb personal baskets, nb group baskets, nb external baskets) tuple for given user""" query1 = "SELECT COUNT(id) FROM bskBASKET WHERE id_owner=%s" res1 = run_sql(query1, (int(uid),)) personal = __wash_sql_count(res1) query2 = """SELECT count(ugbsk.id_bskbasket) FROM usergroup_bskBASKET ugbsk LEFT JOIN user_usergroup uug ON ugbsk.id_usergroup=uug.id_usergroup WHERE uug.id_user=%s AND uug.user_status!=%s GROUP BY ugbsk.id_usergroup""" params = (int(uid), CFG_WEBSESSION_USERGROUP_STATUS['PENDING']) res2 = run_sql(query2, params) if len(res2): groups = reduce(lambda x, y: x + y, map(lambda x: x[0], res2)) else: groups = 0 external = count_external_baskets(uid) return (personal, groups, external) def check_user_owns_baskets(uid, bskids): """ Return 1 if user is owner of every basket in list bskids""" if not((type(bskids) is list) or (type(bskids) is tuple)): bskids = [bskids] query = """SELECT id_owner FROM bskBASKET WHERE %s GROUP BY id_owner""" sep = ' OR ' query %= sep.join(['id=%s'] * len(bskids)) res = run_sql(query, tuple(bskids)) if len(res)==1 and int(res[0][0])==uid: return 1 else: return 0 def get_max_user_rights_on_basket(uid, bskid): """Return the max rights a user has on this basket""" query_owner = "SELECT count(id_owner) FROM bskBASKET WHERE id_owner=%s and id=%s" params_owner = (int(uid), int(bskid)) res = run_sql(query_owner, params_owner) if res and res[0][0]: # if this user is owner of this baskets he can do anything he wants. return CFG_WEBBASKET_SHARE_LEVELS['MANAGE'] # not owner => group member ? query_group_baskets = """ SELECT share_level FROM user_usergroup AS ug LEFT JOIN usergroup_bskBASKET AS ub ON ug.id_usergroup=ub.id_usergroup WHERE ug.id_user=%s AND ub.id_bskBASKET=%s AND NOT(ub.share_level='NO') AND ug.user_status!=%s """ params_group_baskets = (int(uid), int(bskid), CFG_WEBSESSION_USERGROUP_STATUS['PENDING']) res = run_sql(query_group_baskets, params_group_baskets) group_index = None if res: try: group_index = CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(res[0][0]) except: return None # public basket ? query_public_baskets = """ SELECT share_level FROM usergroup_bskBASKET WHERE id_usergroup=0 AND id_bskBASKET=%s """ public_index = None res = run_sql(query_public_baskets, (int(bskid),)) if res: try: public_index = CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(res[0][0]) except: return None if group_index or public_index: if group_index > public_index: return CFG_WEBBASKET_SHARE_LEVELS_ORDERED[group_index] else: return CFG_WEBBASKET_SHARE_LEVELS_ORDERED[public_index] return None ########################### Personal baskets ################################## def get_personal_baskets_info_for_topic(uid, topic): """Return information about every basket that belongs to the given user and topic.""" query = """ SELECT bsk.id, bsk.name, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), bsk.nb_views, count(rec.id_bibrec_or_bskEXTREC), DATE_FORMAT(max(rec.date_added), '%%Y-%%m-%%d %%H:%%i:%%s') FROM user_bskBASKET AS ubsk JOIN bskBASKET AS bsk ON bsk.id=ubsk.id_bskBASKET AND bsk.id_owner=%s LEFT JOIN bskREC AS rec ON rec.id_bskBASKET=bsk.id WHERE ubsk.id_user=%s AND ubsk.topic=%s GROUP BY bsk.id ORDER BY bsk.name""" params = (uid, uid, topic) res = run_sql(query, params) return res def get_all_personal_basket_ids_and_names_by_topic(uid): """For a given user return all their personal baskets (in tuples: (id, name)) grouped by topic. Note that the basket tuples have to evaluated to be converted to actual tuples.""" query = """ SELECT ubsk.topic, count(bsk.id), GROUP_CONCAT('(', bsk.id, ', \"', bsk.name, '\")' ORDER BY bsk.name) FROM user_bskBASKET AS ubsk JOIN bskBASKET AS bsk ON ubsk.id_bskBASKET=bsk.id AND ubsk.id_user=bsk.id_owner WHERE bsk.id_owner=%s GROUP BY ubsk.topic ORDER BY ubsk.topic""" params = (uid,) res = run_sql(query, params) return res def get_all_personal_basket_ids_and_names_by_topic_for_add_to_list(uid): """For a given user return all their personal baskets (in tuples: (id, name)) grouped by topic. Note that the basket tuples have to evaluated to be converted to actual tuples.""" query = """ SELECT ubsk.topic, GROUP_CONCAT('(', bsk.id, ', \"', bsk.name, '\")' ORDER BY bsk.name) FROM user_bskBASKET AS ubsk JOIN bskBASKET AS bsk ON ubsk.id_bskBASKET=bsk.id AND ubsk.id_user=bsk.id_owner WHERE bsk.id_owner=%s GROUP BY ubsk.topic ORDER BY ubsk.topic""" params = (uid,) res = run_sql(query, params) return res def get_all_personal_baskets_names(uid): """ for a given user, returns every basket he is owner of returns list of tuples: (bskid, bsk_name, topic) """ query = """ SELECT bsk.id, bsk.name, ubsk.topic FROM user_bskBASKET ubsk JOIN bskBASKET bsk ON ubsk.id_bskBASKET=bsk.id AND ubsk.id_user=bsk.id_owner WHERE bsk.id_owner=%s ORDER BY ubsk.topic """ params = (int(uid),) return run_sql(query, params) def get_basket_name(bskid): """return the name of a given basket""" query = 'SELECT name FROM bskBASKET where id=%s' res = run_sql(query, (int(bskid), )) if res: return res[0][0] else: return '' def is_personal_basket_valid(uid, bskid): """Check if the basked (bskid) belongs to user (uid) and is valid.""" query = """ SELECT id FROM bskBASKET WHERE id=%s AND id_owner=%s""" params = (bskid, uid) res = run_sql(query, params) return res def is_topic_valid(uid, topic): """Check if the topic defined by user (uid) exists.""" query = """ SELECT distinct(topic) FROM user_bskBASKET WHERE topic=%s AND id_user=%s""" params = (topic, uid) res = run_sql(query, params) return res def get_basket_topic(uid, bskid): """Return the name of the topic this basket (bskid) belongs to.""" query = """ SELECT topic FROM user_bskBASKET WHERE id_bskBASKET=%s AND id_user=%s""" params = (bskid,uid) res = run_sql(query, params) return res def get_personal_topics_infos(uid): """ Get the list of every topic user has defined, and the number of baskets in each topic @param uid: user id (int) @return: a list of tuples (topic name, nb of baskets) """ query = """SELECT topic, count(b.id) FROM user_bskBASKET ub JOIN bskBASKET b ON ub.id_bskBASKET=b.id AND b.id_owner=ub.id_user WHERE ub.id_user=%s GROUP BY topic ORDER BY topic""" uid = int(uid) res = run_sql(query, (uid,)) return res def rename_basket(bskid, new_name): """Rename basket to new_name""" run_sql("UPDATE bskBASKET SET name=%s WHERE id=%s", (new_name, bskid)) def rename_topic(uid, old_topic, new_topic): """Rename topic to new_topic """ res = run_sql("UPDATE user_bskBASKET SET topic=%s WHERE id_user=%s AND topic=%s", (new_topic, uid, old_topic)) return res def move_baskets_to_topic(uid, bskids, new_topic): """Move given baskets to another topic""" if not((type(bskids) is list) or (type(bskids) is tuple)): bskids = [bskids] query = "UPDATE user_bskBASKET SET topic=%s WHERE id_user=%s AND (" query += ' OR '.join(['id_bskBASKET=%s'] * len(bskids)) query += ")" params = (new_topic, uid) + tuple(bskids) res = run_sql(query, params) return res def delete_basket(bskid): """Delete given basket.""" # TODO: check if any alerts are automaticly adding items to the given basket. bskid = int(bskid) query1 = "DELETE FROM bskBASKET WHERE id=%s" res = run_sql(query1, (bskid,)) query2A = "SELECT id_bibrec_or_bskEXTREC FROM bskREC WHERE id_bskBASKET=%s" local_and_external_ids = run_sql(query2A, (bskid,)) external_ids = [local_and_external_id[0] for local_and_external_id in \ local_and_external_ids if local_and_external_id[0]<0] for external_id in external_ids: delete_item(bskid=bskid, recid=external_id, update_date_modification=False) query2B = "DELETE FROM bskREC WHERE id_bskBASKET=%s" run_sql(query2B, (bskid,)) query3 = "DELETE FROM bskRECORDCOMMENT WHERE id_bskBASKET=%s" run_sql(query3, (bskid,)) query4 = "DELETE FROM user_bskBASKET WHERE id_bskBASKET=%s" run_sql(query4, (bskid,)) query5 = "DELETE FROM usergroup_bskBASKET WHERE id_bskBASKET=%s" run_sql(query5, (bskid,)) query6 = "DELETE FROM user_query_basket WHERE id_basket=%s" run_sql(query6, (bskid,)) return int(res) def create_basket(uid, basket_name, topic): """Create new basket for given user in given topic""" now = convert_datestruct_to_datetext(localtime()) id_bsk = run_sql("""INSERT INTO bskBASKET (id_owner, name, date_modification) VALUES (%s, %s, %s)""", (uid, basket_name, now)) run_sql("""INSERT INTO user_bskBASKET (id_user, id_bskBASKET, topic) VALUES (%s, %s, %s)""", (uid, id_bsk, topic)) return id_bsk def get_all_items_in_user_personal_baskets(uid, topic="", format='hb'): """For the specified user, return all the items in their personal baskets, grouped by basket if local or as a list if external. If topic is set, return only that topic's items.""" if topic: topic_clause = """AND ubsk.topic=%s""" params_local = (uid, uid, topic) params_external = (uid, uid, topic, format) else: topic_clause = "" params_local = (uid, uid) params_external = (uid, uid, format) query_local = """ SELECT rec.id_bskBASKET, bsk.name, ubsk.topic, GROUP_CONCAT(rec.id_bibrec_or_bskEXTREC) FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET AND bsk.id_owner=%%s JOIN user_bskBASKET AS ubsk ON ubsk.id_bskBASKET=rec.id_bskBASKET AND ubsk.id_user=%%s %s WHERE rec.id_bibrec_or_bskEXTREC > 0 GROUP BY rec.id_bskBASKET""" % (topic_clause,) res_local = run_sql(query_local, params_local) query_external = """ SELECT rec.id_bskBASKET, bsk.name, ubsk.topic, rec.id_bibrec_or_bskEXTREC, ext.value FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET AND bsk.id_owner=%%s JOIN user_bskBASKET AS ubsk ON ubsk.id_bskBASKET=rec.id_bskBASKET AND ubsk.id_user=%%s %s JOIN bskEXTFMT AS ext ON ext.id_bskEXTREC=-rec.id_bibrec_or_bskEXTREC AND ext.format=%%s WHERE rec.id_bibrec_or_bskEXTREC < 0 ORDER BY rec.id_bskBASKET""" % (topic_clause,) res_external = run_sql(query_external, params_external) return (res_local, res_external) def get_all_items_in_user_personal_baskets_by_matching_notes(uid, topic="", p=""): """For the specified user, return all the items in their personal baskets matching their notes' titles and bodies, grouped by basket. If topic is set, return only that topic's items.""" p = p and '%' + p + '%' or '%' if topic: topic_clause = """AND ubsk.topic=%s""" params = (uid, uid, topic, p, p) else: topic_clause = "" params = (uid, uid, p, p) query = """ SELECT notes.id_bskBASKET, bsk.name, ubsk.topic, GROUP_CONCAT(DISTINCT(notes.id_bibrec_or_bskEXTREC)) FROM bskRECORDCOMMENT AS notes JOIN bskBASKET AS bsk ON bsk.id=notes.id_bskBASKET AND bsk.id_owner=%%s JOIN user_bskBASKET AS ubsk ON ubsk.id_bskBASKET=notes.id_bskBASKET AND ubsk.id_user=%%s %s WHERE notes.title like %%s OR notes.body like %%s GROUP BY notes.id_bskBASKET""" % (topic_clause,) res = run_sql(query, params) return res def get_all_user_topics(uid): """Return a list of the user's topics.""" query = """ SELECT ubsk.topic FROM bskBASKET AS bsk JOIN user_bskBASKET AS ubsk ON ubsk.id_bskBASKET=bsk.id AND ubsk.id_user=bsk.id_owner WHERE bsk.id_owner=%s GROUP BY ubsk.topic""" params = (uid,) res = run_sql(query, params) return res ########################## Actions on baskets ################################# def get_basket_record(bskid, recid, format='hb'): """get record recid in basket bskid """ if recid < 0: rec_table = 'bskEXTREC' format_table = 'bskEXTFMT' id_field = 'id_bskEXTREC' sign = '-' else: rec_table = 'bibrec' format_table = 'bibfmt' id_field = 'id_bibrec' sign = '' query = """ SELECT DATE_FORMAT(record.creation_date, '%%%%Y-%%%%m-%%%%d %%%%H:%%%%i:%%%%s'), DATE_FORMAT(record.modification_date, '%%%%Y-%%%%m-%%%%d %%%%H:%%%%i:%%%%s'), DATE_FORMAT(bskREC.date_added, '%%%%Y-%%%%m-%%%%d %%%%H:%%%%i:%%%%s'), user.nickname, count(cmt.id_bibrec_or_bskEXTREC), DATE_FORMAT(max(cmt.date_creation), '%%%%Y-%%%%m-%%%%d %%%%H:%%%%i:%%%%s'), fmt.value FROM bskREC LEFT JOIN user ON bskREC.id_user_who_added_item=user.id LEFT JOIN bskRECORDCOMMENT cmt ON bskREC.id_bibrec_or_bskEXTREC=cmt.id_bibrec_or_bskEXTREC LEFT JOIN %(rec_table)s record ON (%(sign)sbskREC.id_bibrec_or_bskEXTREC=record.id) LEFT JOIN %(format_table)s fmt ON (record.id=fmt.%(id_field)s) WHERE bskREC.id_bskBASKET=%%s AND bskREC.id_bibrec_or_bskEXTREC=%%s AND fmt.format=%%s GROUP BY bskREC.id_bibrec_or_bskEXTREC """ % {'rec_table': rec_table, 'sign': sign, 'format_table': format_table, 'id_field':id_field} params = (int(bskid), int(recid), format) res = run_sql(query, params) if res: return __decompress_last(res[0]) return () def get_basket_content(bskid, format='hb'): """Get all records for a given basket.""" query = """ SELECT rec.id_bibrec_or_bskEXTREC, extrec.collection_id, count(cmt.id_bibrec_or_bskEXTREC), DATE_FORMAT(max(cmt.date_creation), '%%Y-%%m-%%d %%H:%%i:%%s'), extern.value as ext_val, intern.value as int_val, rec.score FROM bskREC AS rec LEFT JOIN bskRECORDCOMMENT AS cmt ON (rec.id_bibrec_or_bskEXTREC=cmt.id_bibrec_or_bskEXTREC AND rec.id_bskBASKET=cmt.id_bskBASKET) LEFT JOIN bskEXTFMT AS extern ON (-rec.id_bibrec_or_bskEXTREC=extern.id_bskEXTREC AND extern.format=%s) LEFT JOIN bibfmt AS intern ON (rec.id_bibrec_or_bskEXTREC=intern.id_bibrec AND intern.format=%s) LEFT JOIN bskEXTREC AS extrec ON extrec.id=-rec.id_bibrec_or_bskEXTREC WHERE rec.id_bskBASKET=%s GROUP BY rec.id_bibrec_or_bskEXTREC ORDER BY rec.score""" params = (format, format, int(bskid)) res = run_sql(query, params) if res: query2 = "UPDATE bskBASKET SET nb_views=nb_views+1 WHERE id=%s" run_sql(query2, (int(bskid),)) return res return () def get_basket_item(bskid, recid, format='hb'): """Get item (recid) for a given basket.""" query = """ SELECT rec.id_bibrec_or_bskEXTREC, extrec.collection_id, count(cmt.id_bibrec_or_bskEXTREC), DATE_FORMAT(max(cmt.date_creation), '%%Y-%%m-%%d %%H:%%i:%%s'), extern.value as ext_val, intern.value as int_val, rec.score FROM bskREC rec LEFT JOIN bskRECORDCOMMENT cmt ON (rec.id_bibrec_or_bskEXTREC=cmt.id_bibrec_or_bskEXTREC AND rec.id_bskBASKET=cmt.id_bskBASKET) LEFT JOIN bskEXTFMT extern ON (-rec.id_bibrec_or_bskEXTREC=extern.id_bskEXTREC AND extern.format=%s) LEFT JOIN bibfmt intern ON (rec.id_bibrec_or_bskEXTREC=intern.id_bibrec AND intern.format=%s) LEFT JOIN bskEXTREC AS extrec ON extrec.id=-rec.id_bibrec_or_bskEXTREC WHERE rec.id_bskBASKET=%s AND rec.id_bibrec_or_bskEXTREC=%s GROUP BY rec.id_bibrec_or_bskEXTREC ORDER BY rec.score""" params = (format, format, bskid, recid) res = run_sql(query, params) if res: queryU = """UPDATE bskBASKET SET nb_views=nb_views+1 WHERE id=%s""" paramsU = (bskid,) run_sql(queryU, paramsU) score = res[0][6] query_previous = """SELECT id_bibrec_or_bskEXTREC FROM bskREC WHERE id_bskBASKET=%s AND score<%s ORDER BY score DESC LIMIT 1""" params_previous = (bskid, score) res_previous = run_sql(query_previous, params_previous) query_next = """SELECT id_bibrec_or_bskEXTREC FROM bskREC WHERE id_bskBASKET=%s AND score>%s ORDER BY score ASC LIMIT 1""" params_next = (bskid, score) res_next = run_sql(query_next, params_next) query_index = """ SELECT COUNT(id_bibrec_or_bskEXTREC) FROM bskREC WHERE id_bskBASKET=%s AND score<=%s ORDER BY score""" params_index = (bskid, score) res_index = run_sql(query_index, params_index) res_index = __wash_sql_count(res_index) return (res[0], res_previous and res_previous[0][0] or 0, res_next and res_next[0][0] or 0, res_index) else: return () def share_basket_with_group(bskid, group_id, share_level=CFG_WEBBASKET_SHARE_LEVELS['READITM']): """ Share basket bskid with group group_id with given share_level @param share_level: see CFG_WEBBASKET_SHARE_LEVELS in webbasket_config """ now = convert_datestruct_to_datetext(localtime()) run_sql("""REPLACE INTO usergroup_bskBASKET (id_usergroup, id_bskBASKET, date_shared, share_level) VALUES (%s,%s,%s,%s)""", (group_id, bskid, now, str(share_level))) def update_rights(bskid, group_rights): """update rights (permissions) for groups. @param bskid: basket id @param group_rights: dictionary of {group id: new rights} """ now = convert_datestruct_to_datetext(localtime()) query1 = """REPLACE INTO usergroup_bskBASKET (id_usergroup, id_bskBASKET, date_shared, share_level) VALUES """ + \ ', '.join(["(%s, %s, %s, %s)"] * len(group_rights.items())) params = () for (group_id, share_level) in group_rights.items(): params += (int(group_id), int(bskid), now, str(share_level)) run_sql(query1, params) query2 = """DELETE FROM usergroup_bskBASKET WHERE share_level='NO'""" run_sql(query2) def move_item(bskid, recid, direction): """Change score of an item in a basket""" bskid = int(bskid) query1 = """SELECT id_bibrec_or_bskEXTREC, score FROM bskREC WHERE id_bskBASKET=%s ORDER BY score, date_added""" items = run_sql(query1, (bskid,)) (recids, scores) = zip(*items) (recids, scores) = (list(recids), list(scores)) if len(recids) and recid in recids: current_index = recids.index(recid) if direction == CFG_WEBBASKET_ACTIONS['UP']: switch_index = 0 if current_index != 0: switch_index = current_index -1 else: switch_index = len(recids) - 1 if current_index != len(recids)-1: switch_index = current_index + 1 query2 = """UPDATE bskREC SET score=%s WHERE id_bskBASKET=%s AND id_bibrec_or_bskEXTREC=%s""" res1 = run_sql(query2, (scores[switch_index], bskid, recids[current_index])) res2 = run_sql(query2, (scores[current_index], bskid, recids[switch_index])) if res1 and res2: now = convert_datestruct_to_datetext(localtime()) query3 = "UPDATE bskBASKET SET date_modification=%s WHERE id=%s" params3 = (now, int(bskid)) run_sql(query3, params3) def delete_item(bskid, recid, update_date_modification=True): """Remove item recid from basket bskid""" if recid < 0: query0A = "select count(id_bskBASKET) from bskREC where id_bibrec_or_bskEXTREC=%s" % (int(recid)) ncopies = run_sql(query0A) if ncopies and ncopies[0][0]<=1: # uncomment the following 5 lines and comment the following 2 to delete cached records # only for external sources and not for external records #query0B = "SELECT collection_id FROM bskEXTREC WHERE id=%s" % (-int(recid)) #colid = run_sql(query0B) #if colid and colid[0][0]==0: #query0C = "DELETE from bskEXTFMT WHERE id_bskEXTREC=%s" % (-int(recid)) #run_sql(query0C) # the following two lines delete cached external records. We could keep them if we find # a way to reuse them in case the external records are added again in the future. query0D = "DELETE from bskEXTFMT WHERE id_bskEXTREC=%s" % (-int(recid)) run_sql(query0D) query0E = "DELETE from bskEXTREC WHERE id=%s" % (-int(recid)) run_sql(query0E) query_notes = "DELETE FROM bskRECORDCOMMENT WHERE id_bskBASKET=%s AND id_bibrec_or_bskEXTREC=%s" run_sql(query_notes, (bskid, recid,)) query1 = "DELETE from bskREC WHERE id_bskBASKET=%s AND id_bibrec_or_bskEXTREC=%s" params1 = (int(bskid), int(recid)) res = run_sql(query1, params1) if update_date_modification and res: now = convert_datestruct_to_datetext(localtime()) query2 = "UPDATE bskBASKET SET date_modification=%s WHERE id=%s" params2 = (now, int(bskid)) run_sql(query2, params2) return res def add_to_basket(uid, recids=[], colid=0, bskid=0, es_title="", es_desc="", es_url=""): """Add items (recids) basket (bskid).""" if (recids or (colid == -1 and es_title and es_desc and es_url)) and bskid > 0: query_max_score = """ SELECT MAX(score) FROM bskREC WHERE id_bskBASKET=%s""" params_max_score = (bskid,) res_max_score = run_sql(query_max_score, params_max_score) max_score = __wash_sql_count(res_max_score) if not max_score: # max_score == None actually means that the basket doesn't exist. # Maybe we should return 0 and inform the admin? max_score = 1 if colid > 0: query_existing = """ SELECT id, external_id FROM bskEXTREC WHERE %s AND collection_id=%s""" sep_or = ' OR ' query_existing %= (sep_or.join(['external_id=%s'] * len(recids)), colid) params_existing = tuple(recids) res_existing = run_sql(query_existing, params_existing) existing_recids = [int(external_ids_couple[1]) for external_ids_couple in res_existing] existing_ids = [int(ids[0]) for ids in res_existing] new_recids = [recid for recid in recids if int(recid) not in existing_recids] # sets approach #existing_recids = [ids[1] for ids in res_existing] #new_recids = list(set(recids)-set(existing_recids)) if new_recids: query_new = """ INSERT INTO bskEXTREC (external_id, collection_id, creation_date, modification_date) VALUES """ now = convert_datestruct_to_datetext(localtime()) records = ["(%s, %s, %s, %s)"] * len(new_recids) query_new += ', '.join(records) params_new = () for new_recid in new_recids: params_new += (int(new_recid), colid, now, now) res_new = run_sql(query_new, params_new) recids = [-int(recid) for recid in existing_ids] recids.extend(range(-res_new,-(res_new+len(new_recids)),-1)) else: recids = [-int(recid) for recid in existing_ids] elif colid < 0: query_external = """INSERT INTO bskEXTREC (collection_id, original_url, creation_date, modification_date) VALUES (%s, %s, %s, %s)""" now = convert_datestruct_to_datetext(localtime()) params_external = (colid, es_url, now, now) res_external = run_sql(query_external, params_external) recids = [-res_external] store_external_source(res_external, es_title, es_desc, es_url, 'xm') store_external_source(res_external, es_title, es_desc, es_url, 'hb') query_insert = """ INSERT IGNORE INTO bskREC (id_bibrec_or_bskEXTREC, id_bskBASKET, id_user_who_added_item, date_added, score) VALUES """ if colid == 0 or (colid > 0 and not new_recids): now = convert_datestruct_to_datetext(localtime()) records = ["(%s, %s, %s, %s, %s)"] * len(recids) query_insert += ', '.join(records) params_insert = () i = 1 for recid in recids: params_insert += (recid, bskid, uid, now, max_score + i) i += 1 run_sql(query_insert, params_insert) query_update = """ UPDATE bskBASKET SET date_modification=%s WHERE id=%s""" params_update = (now, bskid) run_sql(query_update, params_update) return recids return 0 def add_to_many_baskets(uid, recids=[], colid=0, bskids=[], es_title="", es_desc="", es_url=""): """Add items recids to every basket in bskids list.""" if (len(recids) or colid == -1) and len(bskids): query1 = """SELECT id_bskBASKET, max(score) FROM bskREC WHERE %s GROUP BY id_bskBASKET""" bskids = [bskid for bskid in bskids if int(bskid) >= 0] sep_or = ' OR ' query1 %= sep_or.join(['id_bskBASKET=%s'] * len(bskids)) bsks = dict.fromkeys(bskids, 0) params = tuple(bskids) bsks.update(dict(run_sql(query1, params))) if colid > 0: query2A = """SELECT id, external_id FROM bskEXTREC WHERE %s AND collection_id=%s""" query2A %= (sep_or.join(['external_id=%s'] * len(recids)), colid) params2A = tuple(recids) res2A = run_sql(query2A, params2A) existing_recids = [int(external_ids_couple[1]) for external_ids_couple in res2A] existing_ids = [int(ids[0]) for ids in res2A] new_recids = [recid for recid in recids if int(recid) not in existing_recids] # sets approach #existing_recids = [ids[1] for ids in res2A] #new_recids = list(set(recids)-set(existing_recids)) f = open("/tmp/bsk_db", "w") f.write(str(recids) + "\n" + str(existing_recids) + "\n" + str(existing_ids) + "\n" + str(new_recids) + "\n") f.close() if new_recids: query2B = """INSERT INTO bskEXTREC (external_id, collection_id, creation_date, modification_date) VALUES """ now = convert_datestruct_to_datetext(localtime()) records = ["(%s, %s, %s, %s)"] * len(new_recids) query2B += ', '.join(records) params2B = () for new_recid in new_recids: params2B += (int(new_recid), colid, now, now) res = run_sql(query2B, params2B) recids = [-int(recid) for recid in existing_ids] recids.extend(range(-res,-(res+len(new_recids)),-1)) else: recids = [-int(recid) for recid in existing_ids] elif colid < 0: query2C = """INSERT INTO bskEXTREC (collection_id, original_url, creation_date, modification_date) VALUES (%s, %s, %s, %s)""" now = convert_datestruct_to_datetext(localtime()) params = (colid, es_url, now, now) res = run_sql(query2C, params) recids = [-res] store_external_source(res, es_title, es_desc, es_url, 'xm') store_external_source(res, es_title, es_desc, es_url, 'hb') query2 = """INSERT IGNORE INTO bskREC (id_bibrec_or_bskEXTREC, id_bskBASKET, id_user_who_added_item, date_added, score) VALUES """ if colid == 0 or (colid > 0 and not new_recids): now = convert_datestruct_to_datetext(localtime()) records = ["(%s, %s, %s, %s, %s)"] * (len(recids) * len(bsks.items())) query2 += ', '.join(records) params = () for (bskid, max_score) in bsks.items(): i = 1 for recid in recids: params += (int(recid), int(bskid), int(uid), now, int(max_score) + i) i += 1 run_sql(query2, params) query3 = """UPDATE bskBASKET SET date_modification=%s WHERE """ query3 += sep_or.join(["id=%s"] * len(bskids)) params = (now,) + tuple(bskids) run_sql(query3, params) return len(bskids) return 0 def get_external_records_by_collection(recids): """Get the selected recids, both local and external, grouped by collection.""" if recids: query = """ SELECT GROUP_CONCAT(id), GROUP_CONCAT(external_id), collection_id FROM bskEXTREC WHERE %s GROUP BY collection_id""" recids = [-recid for recid in recids] sep_or = ' OR ' query %= sep_or.join(['id=%s'] * len(recids)) params = tuple(recids) res = run_sql(query,params) return res return 0 def get_external_records(recids, of="hb"): """Get formatted external records from the database.""" if recids: query = """ SELECT rec.collection_id, fmt.id_bskEXTREC, fmt.value FROM bskEXTFMT AS fmt JOIN bskEXTREC AS rec ON rec.id=fmt.id_bskEXTREC WHERE format=%%s AND ( %s )""" recids = [-recid for recid in recids] sep_or = ' OR ' query %= sep_or.join(['id_bskEXTREC=%s'] * len(recids)) params = [of] params.extend(recids) params = tuple(params) res = run_sql(query,params) return res return () def store_external_records(records, of="hb"): """Store formatted external records to the database.""" if records: query = """INSERT INTO bskEXTFMT (id_bskEXTREC, format, last_updated, value) VALUES """ now = convert_datestruct_to_datetext(localtime()) formatted_records = ["(%s, %s, %s, %s)"] * len(records) query += ', '.join(formatted_records) params = () for record in records: params += (record[0], of, now, compress(record[1])) run_sql(query,params) def store_external_urls(ids_urls): """Store original urls for external records to the database.""" #for id_url in ids_urls.iteritems(): for id_url in ids_urls: query = """UPDATE bskEXTREC SET original_url=%s WHERE id=%s""" params = (id_url[1], id_url[0]) run_sql(query,params) def store_external_source(es_id, es_title, es_desc, es_url, of="hb"): """Store formatted external sources to the database.""" if es_id and es_title and es_desc: query = """INSERT INTO bskEXTFMT (id_bskEXTREC, format, last_updated, value) VALUES (%s, %s, %s, %s)""" now = convert_datestruct_to_datetext(localtime()) value = create_pseudo_record(es_title, es_desc, es_url, of) params = (es_id, of, now, compress(value)) run_sql(query,params) def get_external_colid_and_url(recid): """Get the collection id and original url for an external record.""" if recid: query = """SELECT collection_id, original_url FROM bskEXTREC WHERE id=%s""" params = (-recid,) res = run_sql(query,params) if res: return res else: return 0 ############################ Group baskets #################################### def get_group_baskets_info_for_group(grpid): """Return information about every basket that belongs to the given group, provided the user is its manager or a member of it.""" if not grpid: return () query = """ SELECT bsk.id, bsk.name, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), bsk.nb_views, COUNT(rec.id_bibrec_or_bskEXTREC), DATE_FORMAT(max(rec.date_added), '%%Y-%%m-%%d %%H:%%i:%%s'), ugbsk.share_level, bsk.id_owner FROM usergroup_bskBASKET AS ugbsk JOIN bskBASKET AS bsk ON bsk.id=ugbsk.id_bskBASKET LEFT JOIN bskREC AS rec ON rec.id_bskBASKET=bsk.id WHERE ugbsk.id_usergroup=%s AND ugbsk.share_level!='NO' GROUP BY bsk.id ORDER BY bsk.name""" params = (grpid,) res = run_sql(query, params) return res def get_group_name(gid): """Given its id return the group's name.""" query = """ SELECT name FROM usergroup WHERE id=%s""" params = (gid,) res = run_sql(query, params) return res def get_all_group_basket_ids_and_names_by_group(uid): """For a given user return all their group baskets (in tuples: (id, name)) grouped by group. Note that the basket tuples have to evaluated to be converted to actual tuples.""" query = """ SELECT ug.id, ug.name, count(bsk.id), GROUP_CONCAT('(', ugbsk.id_bskBASKET, ', \"', bsk.name, '\")' ORDER BY bsk.name) FROM usergroup AS ug JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_usergroup=ug.id JOIN bskBASKET AS bsk ON ugbsk.id_bskBASKET=bsk.id JOIN user_usergroup AS uug ON ug.id=uug.id_usergroup AND uug.id_user=%s GROUP BY ug.name ORDER BY ug.name""" params = (uid,) res = run_sql(query, params) return res def get_all_group_basket_ids_and_names_by_group_for_add_to_list(uid): """For a given user return all their group baskets (in tuples: (id, name)) grouped by group. Note that the basket tuples have to evaluated to be converted to actual tuples.""" query = """ SELECT ug.name, GROUP_CONCAT('(', ugbsk.id_bskBASKET, ', \"', bsk.name, '\")' ORDER BY bsk.name) FROM usergroup AS ug JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_usergroup=ug.id AND ugbsk.share_level!='NO' AND ugbsk.share_level!='RI' AND ugbsk.share_level!='RC' AND ugbsk.share_level!='AC' JOIN bskBASKET AS bsk ON ugbsk.id_bskBASKET=bsk.id JOIN user_usergroup AS uug ON ug.id=uug.id_usergroup AND uug.id_user=%s GROUP BY ug.name ORDER BY ug.name""" params = (uid,) res = run_sql(query, params) return res def get_all_group_baskets_names(uid, min_rights=CFG_WEBBASKET_SHARE_LEVELS['ADDCMT']): """For a given user returns every group baskets in which he can <min_rights> return a list of tuples: (bskid, bsk_name, group_name).""" # TODO: This function is no longer used. Delete if necessary. uid = int(uid) try: min_rights_num = CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(min_rights) except ValueError: return () groups = get_groups_user_member_of(uid) if groups: where_clause = '(' where_clause += " OR ".join(["ugbsk.id_usergroup=%s"] * len(groups)) where_clause += ') AND (' where_clause += " OR ".join(["ugbsk.share_level=%s"] * len(CFG_WEBBASKET_SHARE_LEVELS_ORDERED[min_rights_num:])) where_clause += ")" query = """ SELECT bsk.id, bsk.name, ug.name FROM usergroup ug JOIN usergroup_bskBASKET ugbsk ON ug.id=ugbsk.id_usergroup JOIN bskBASKET bsk ON bsk.id=ugbsk.id_bskBASKET WHERE %s AND NOT(ugbsk.share_level='NO') ORDER BY ug.name""" % where_clause params = tuple([group_id for (group_id, dummy) in groups]) params += tuple(CFG_WEBBASKET_SHARE_LEVELS_ORDERED[min_rights_num:]) return run_sql(query, params) return () def is_shared_to(bskids): """For each bskid in bskids get id of one of its group. Used to make distinction between private basket (no group), 'world' basket (0) or group basket (any int > 0) """ if not((type(bskids) == list) or (type(bskids) == tuple)): bskids = [bskids] query = """SELECT b.id, min(u.id_usergroup) FROM bskBASKET b LEFT JOIN usergroup_bskBASKET u ON (b.id=u.id_bskBASKET) """ if len(bskids) != 0: query += " WHERE " query += " OR ".join(['b.id=%s'] * len(bskids)) query += " GROUP BY b.id" params = tuple(bskids) res = run_sql(query, params) if res: return res return () def get_basket_share_level(bskid): """Get the minimum share level of the basket (bskid). Returns: None for personal baskets positive integet for group baskets 0 for public baskets Will return 0 if the basket is both group and publicly shared.""" query = """ SELECT MIN(ugbsk.id_usergroup) FROM bskBASKET AS bsk LEFT JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=bsk.id WHERE bsk.id=%s GROUP BY bsk.id""" params = (bskid,) res = run_sql(query, params) return res def get_all_items_in_user_group_baskets(uid, group=0, format='hb'): """For the specified user, return all the items in their group baskets, grouped by basket if local or as a list if external. If group is set, return only that group's items.""" if group: group_clause = """AND ubsk.id_usergroup=%s""" params_local = (group, uid) params_external = (group, uid, format) else: group_clause = "" params_local = (uid,) params_external = (uid, format) query_local = """ SELECT rec.id_bskBASKET, bsk.name, uug.id_usergroup, ug.name, GROUP_CONCAT(rec.id_bibrec_or_bskEXTREC) FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET JOIN usergroup_bskBASKET AS ubsk ON ubsk.id_bskBASKET=rec.id_bskBASKET %s JOIN user_usergroup AS uug ON uug.id_usergroup=ubsk.id_usergroup AND uug.id_user=%%s JOIN usergroup AS ug ON ug.id=uug.id_usergroup WHERE rec.id_bibrec_or_bskEXTREC > 0 GROUP BY rec.id_bskBASKET""" % (group_clause,) res_local = run_sql(query_local, params_local) query_external = """ SELECT rec.id_bskBASKET, bsk.name, uug.id_usergroup, ug.name, rec.id_bibrec_or_bskEXTREC, ext.value FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET JOIN usergroup_bskBASKET AS ubsk ON ubsk.id_bskBASKET=rec.id_bskBASKET %s JOIN user_usergroup AS uug ON uug.id_usergroup=ubsk.id_usergroup AND uug.id_user=%%s JOIN usergroup AS ug ON ug.id=uug.id_usergroup JOIN bskEXTFMT AS ext ON ext.id_bskEXTREC=-rec.id_bibrec_or_bskEXTREC AND ext.format=%%s WHERE rec.id_bibrec_or_bskEXTREC < 0 ORDER BY rec.id_bskBASKET""" % (group_clause,) res_external = run_sql(query_external, params_external) return (res_local, res_external) def get_all_items_in_user_group_baskets_by_matching_notes(uid, group=0, p=""): """For the specified user, return all the items in group personal baskets matching their notes' titles and bodies, grouped by basket. If topic is set, return only that topic's items.""" p = p and '%' + p + '%' or '%' if group: group_clause = """AND ugbsk.id_usergroup=%s""" params = (group, uid, p, p) else: group_clause = "" params = (uid, p, p) query = """ SELECT notes.id_bskBASKET, bsk.name, uug.id_usergroup, ug.name, GROUP_CONCAT(DISTINCT(notes.id_bibrec_or_bskEXTREC)) FROM bskRECORDCOMMENT AS notes JOIN bskBASKET AS bsk ON bsk.id=notes.id_bskBASKET JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=notes.id_bskBASKET AND ugbsk.share_level IS NOT NULL AND ugbsk.share_level!='NO' AND ugbsk.share_level!='RI' %s JOIN user_usergroup AS uug ON uug.id_usergroup=ugbsk.id_usergroup AND uug.id_user=%%s JOIN usergroup AS ug ON ug.id=uug.id_usergroup WHERE notes.title like %%s OR notes.body like %%s GROUP BY notes.id_bskBASKET""" % (group_clause,) res = run_sql(query, params) return res def is_group_basket_valid(uid, bskid): """Check if the basked (bskid) belongs to one of the groups the user (uid) is a member of and is valid.""" query = """ SELECT id FROM bskBASKET AS bsk JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=bsk.id JOIN user_usergroup AS uug ON uug.id_usergroup=ugbsk.id_usergroup AND uug.id_user=%s WHERE id=%s""" params = (uid, bskid) res = run_sql(query, params) return res def is_group_valid(uid, group): """Check if the group exists and the user is a member or manager.""" query = """ SELECT id_usergroup FROM user_usergroup WHERE id_usergroup=%s AND id_user=%s""" params = (group, uid) res = run_sql(query, params) return res def get_all_user_groups(uid): """Return a list of the groups the user is a member of or manages.""" query = """ SELECT ug.id, ug.name FROM user_usergroup AS uug JOIN usergroup AS ug ON ug.id=uug.id_usergroup JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_usergroup=uug.id_usergroup WHERE uug.id_user=%s GROUP BY uug.id_usergroup""" params = (uid,) res = run_sql(query, params) return res ########################## External baskets ################################### def get_external_baskets_infos(uid): """Get general informations about every external basket user uid has subscribed to.""" query = """ SELECT bsk.id, bsk.name, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), bsk.nb_views, count(rec.id_bibrec_or_bskEXTREC), DATE_FORMAT(max(rec.date_added), '%%Y-%%m-%%d %%H:%%i:%%s'), ugbsk.share_level FROM bskBASKET bsk JOIN user_bskBASKET ubsk ON (bsk.id=ubsk.id_bskBASKET AND ubsk.id_user=%s) LEFT JOIN bskREC rec ON (bsk.id=rec.id_bskBASKET) LEFT JOIN usergroup_bskBASKET ugbsk ON (ugbsk.id_bskBASKET=bsk.id AND ugbsk.id_usergroup=0) WHERE bsk.id_owner!=%s GROUP BY bsk.id """ uid = int(uid) params = (uid, uid) res = run_sql(query, params) if res: return res return () def get_external_basket_info(bskid): """""" query = """ SELECT bsk.id, bsk.name, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), bsk.nb_views, count(rec.id_bibrec_or_bskEXTREC), DATE_FORMAT(max(rec.date_added), '%%Y-%%m-%%d %%H:%%i:%%s'), ugbsk.share_level FROM bskBASKET AS bsk LEFT JOIN bskREC AS rec ON bsk.id=rec.id_bskBASKET JOIN usergroup_bskBASKET AS ugbsk ON bsk.id=ugbsk.id_bskBASKET AND ugbsk.id_usergroup=0 - JOIN user_bskBASKET AS ubsk - ON bsk.id_owner!=ubsk.id_user - AND bsk.id=ubsk.id_bskBASKET - WHERE id=%s - GROUP BY bsk.id""" + WHERE id=%s""" params = (bskid,) res = run_sql(query, params) return res def get_all_external_basket_ids_and_names(uid): """For a given user return all their external baskets (in tuples: (id, name, number_of_records)).""" query = """ SELECT bsk.id, bsk.name, count(rec.id_bibrec_or_bskEXTREC), ugbsk.id_usergroup FROM user_bskBASKET AS ubsk JOIN bskBASKET AS bsk ON ubsk.id_bskBASKET=bsk.id AND ubsk.id_user!=bsk.id_owner LEFT JOIN bskREC AS rec ON ubsk.id_bskBASKET=rec.id_bskBASKET LEFT JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_usergroup=0 AND ugbsk.id_bskBASKET=bsk.id WHERE ubsk.id_user=%s GROUP BY bsk.id ORDER BY bsk.name""" params = (uid,) res = run_sql(query, params) return res def count_external_baskets(uid): """Returns the number of external baskets the user is subscribed to.""" query = """ SELECT COUNT(ubsk.id_bskBASKET) FROM user_bskBASKET ubsk LEFT JOIN bskBASKET bsk ON (bsk.id=ubsk.id_bskBASKET AND ubsk.id_user=%s) WHERE bsk.id_owner!=%s""" params = (int(uid), int(uid)) res = run_sql(query, params) return __wash_sql_count(res) def get_all_external_baskets_names(uid, min_rights=CFG_WEBBASKET_SHARE_LEVELS['ADDCMT']): """ for a given user returns every basket which he has subscribed to and in which he can <min_rights> return a list of tuples: (bskid, bsk_name) """ uid = int(uid) try: min_rights_num = CFG_WEBBASKET_SHARE_LEVELS_ORDERED.index(min_rights) except ValueError: return () where_clause = ' AND (' for right in CFG_WEBBASKET_SHARE_LEVELS_ORDERED[min_rights_num:-1]: where_clause += "ugbsk.share_level = '%s' OR " % right where_clause += "ugbsk.share_level = '%s')" % CFG_WEBBASKET_SHARE_LEVELS_ORDERED[-1] query = """ SELECT bsk.id, bsk.name FROM bskBASKET bsk JOIN usergroup_bskBASKET ugbsk ON bsk.id=ugbsk.id_bskBASKET JOIN user_bskBASKET ubsk ON ubsk.id_bskBASKET=bsk.id WHERE ugbsk.id_usergroup=0 AND ubsk.id_user=%s AND NOT(bsk.id_owner=%s) AND NOT(ugbsk.share_level='NO') """ + where_clause params = (uid, uid) return run_sql(query, params) def get_all_items_in_user_public_baskets(uid, format='hb'): """For the specified user, return all the items in the public baskets they are subscribed to, grouped by basket if local or as a list if external.""" query_local = """ SELECT rec.id_bskBASKET, bsk.name, GROUP_CONCAT(rec.id_bibrec_or_bskEXTREC) FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET AND bsk.id_owner!=%s JOIN user_bskBASKET AS ubsk ON ubsk.id_bskBASKET=rec.id_bskBASKET AND ubsk.id_user=%s JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=rec.id_bskBASKET AND ugbsk.id_usergroup=0 WHERE rec.id_bibrec_or_bskEXTREC > 0 GROUP BY rec.id_bskBASKET""" params_local = (uid, uid) res_local = run_sql(query_local, params_local) query_external = """ SELECT rec.id_bskBASKET, bsk.name, rec.id_bibrec_or_bskEXTREC, ext.value FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET AND bsk.id_owner!=%s JOIN user_bskBASKET AS ubsk ON ubsk.id_bskBASKET=rec.id_bskBASKET AND ubsk.id_user=%s JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=rec.id_bskBASKET AND ugbsk.id_usergroup=0 JOIN bskEXTFMT AS ext ON ext.id_bskEXTREC=-rec.id_bibrec_or_bskEXTREC AND ext.format=%s WHERE rec.id_bibrec_or_bskEXTREC < 0 ORDER BY rec.id_bskBASKET""" params_external = (uid, uid, format) res_external = run_sql(query_external, params_external) return (res_local, res_external) def get_all_items_in_user_public_baskets_by_matching_notes(uid, p=""): """For the specified user, return all the items in the public baskets they are subscribed to, matching their notes' titles and bodies, grouped by basket""" p = p and '%' + p + '%' or '%' query = """ SELECT notes.id_bskBASKET, bsk.name, GROUP_CONCAT(DISTINCT(notes.id_bibrec_or_bskEXTREC)) FROM bskRECORDCOMMENT AS notes JOIN bskBASKET AS bsk ON bsk.id=notes.id_bskBASKET AND bsk.id_owner!=%s JOIN user_bskBASKET AS ubsk ON ubsk.id_bskBASKET=notes.id_bskBASKET AND ubsk.id_user=%s JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=notes.id_bskBASKET AND ugbsk.id_usergroup=0 AND ugbsk.share_level IS NOT NULL AND ugbsk.share_level!='NO' AND ugbsk.share_level!='RI' WHERE notes.title like %s OR notes.body like %s GROUP BY notes.id_bskBASKET""" params = (uid, uid, p, p) res = run_sql(query, params) return res def get_all_items_in_all_public_baskets(format='hb'): """Return all the items in all the public baskets, grouped by basket if local or as a list if external.""" query_local = """ SELECT rec.id_bskBASKET, bsk.name, GROUP_CONCAT(rec.id_bibrec_or_bskEXTREC) FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=rec.id_bskBASKET AND ugbsk.id_usergroup=0 WHERE rec.id_bibrec_or_bskEXTREC > 0 GROUP BY rec.id_bskBASKET""" res_local = run_sql(query_local) query_external = """ SELECT rec.id_bskBASKET, bsk.name, rec.id_bibrec_or_bskEXTREC, ext.value FROM bskREC AS rec JOIN bskBASKET AS bsk ON bsk.id=rec.id_bskBASKET JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=rec.id_bskBASKET AND ugbsk.id_usergroup=0 JOIN bskEXTFMT AS ext ON ext.id_bskEXTREC=-rec.id_bibrec_or_bskEXTREC AND ext.format=%s WHERE rec.id_bibrec_or_bskEXTREC < 0 ORDER BY rec.id_bskBASKET""" params_external = (format,) res_external = run_sql(query_external, params_external) return (res_local, res_external) def get_all_items_in_all_public_baskets_by_matching_notes(p=""): """Return all the items in all the public baskets matching their notes' titles and bodies, grouped by basket""" p = p and '%' + p + '%' or '%' query = """ SELECT notes.id_bskBASKET, bsk.name, GROUP_CONCAT(DISTINCT(notes.id_bibrec_or_bskEXTREC)) FROM bskRECORDCOMMENT AS notes JOIN bskBASKET AS bsk ON bsk.id=notes.id_bskBASKET JOIN usergroup_bskBASKET AS ugbsk ON ugbsk.id_bskBASKET=notes.id_bskBASKET AND ugbsk.id_usergroup=0 AND ugbsk.share_level IS NOT NULL AND ugbsk.share_level!='NO' AND ugbsk.share_level!='RI' WHERE notes.title like %s OR notes.body like %s GROUP BY notes.id_bskBASKET""" params = (p, p) res = run_sql(query, params) return res ############################ Public access #################################### def get_public_basket_infos(bskid): """return (id, name, date modification, nb of views, id of owner, nickname of owner, rights for public access) for a given basket""" basket = [] query1 = """SELECT bsk.id, bsk.name, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), bsk.nb_views, bsk.id_owner, user.nickname FROM bskBASKET bsk LEFT JOIN user ON bsk.id_owner=user.id WHERE bsk.id=%s""" res1 = run_sql(query1, (int(bskid),)) if len(res1): basket = list(res1[0]) query2 = """SELECT share_level FROM usergroup_bskBASKET WHERE id_usergroup=0 and id_bskBASKET=%s""" res2 = run_sql(query2, (int(bskid),)) if res2: basket.append(res2[0][0]) else: basket.append(None) return basket def get_public_basket_info(bskid): """Return information about a given public basket.""" query = """ SELECT bsk.id, bsk.name, bsk.id_owner, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), bsk.nb_views, COUNT(rec.id_bibrec_or_bskEXTREC), GROUP_CONCAT(rec.id_bibrec_or_bskEXTREC), ubsk.share_level FROM bskBASKET AS bsk LEFT JOIN bskREC AS rec ON rec.id_bskBASKET=bsk.id JOIN usergroup_bskBASKET AS ubsk ON ubsk.id_bskBASKET=bsk.id AND ubsk.id_usergroup=0 WHERE bsk.id=%s GROUP BY bsk.id;""" params = (bskid,) res = run_sql(query, params) return res def get_basket_general_infos(bskid): """return information about a basket, suited for public access. @return: a (id, name, date of modification, nb of views, nb of records, id of owner) tuple """ query = """SELECT bsk.id, bsk.name, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), bsk.nb_views, count(rec.id_bibrec_or_bskEXTREC), bsk.id_owner FROM bskBASKET bsk LEFT JOIN bskREC rec ON bsk.id=rec.id_bskBASKET WHERE bsk.id=%s GROUP BY bsk.id""" res = run_sql(query, (int(bskid),)) if res: query2 = "UPDATE bskBASKET SET nb_views=nb_views+1 WHERE id=%s" run_sql(query2, (int(bskid),)) return res[0] return () def get_basket_owner_id(bskid): """Return the uid of the owner.""" query = """SELECT id_owner FROM bskBASKET WHERE id=%s""" res = run_sql(query, (bskid, )) if res: return res[0][0] return -1 def count_public_baskets(): """Returns the number of public baskets.""" query = """ SELECT COUNT(id_bskBASKET) FROM usergroup_bskBASKET WHERE id_usergroup=0""" res = run_sql(query) return __wash_sql_count(res) def get_public_baskets_list(inf_limit, max_number, order=1, asc=1): """Return list of public baskets @param inf_limit: limit to baskets from number x @param max_number: number of baskets to return @order: 1: order by name of basket, 2: number of views, 3: owner @return: [(basket id, basket name, nb of views, uid of owner, nickname of owner)]""" query = """SELECT bsk.id, bsk.name, bsk.nb_views, u.id, u.nickname FROM bskBASKET bsk LEFT JOIN usergroup_bskBASKET ugbsk on bsk.id=ugbsk.id_bskBASKET LEFT JOIN user u on bsk.id_owner=u.id WHERE ugbsk.id_usergroup=0 """ if order == 2: query += 'ORDER BY bsk.nb_views' elif order == 3: query += 'ORDER BY u.nickname' if asc: query += ' ASC' else: query += ' DESC' query += ', u.id' else: query += 'ORDER BY bsk.name' if asc: query += ' ASC ' else: query += ' DESC ' query += "LIMIT %s,%s" return run_sql(query, (inf_limit, max_number)) def count_all_public_baskets(): """Return the number of all the public baskets.""" query = """ SELECT count(id_bskBASKET) FROM usergroup_bskBASKET WHERE id_usergroup=0""" res = run_sql(query) return __wash_sql_count(res) def get_list_public_baskets(page, max_number, sort='name', asc=1): """Return list of public baskets @param page: limit to baskets from number x @param max_number: maximum number of baskets to return @sort: 1: order by name of basket, 2: number of views, 3: owner @return: [(basket id, basket name, nb of views, uid of owner, nickname of owner)]""" query = """ SELECT bsk.id, bsk.name, bsk.id_owner, u.nickname, DATE_FORMAT(bsk.date_modification, '%%Y-%%m-%%d %%H:%%i:%%s'), COUNT(rec.id_bibrec_or_bskEXTREC) AS items, bsk.nb_views FROM usergroup_bskBASKET AS ugbsk JOIN bskBASKET AS bsk ON bsk.id=ugbsk.id_bskBASKET LEFT JOIN bskREC AS rec ON rec.id_bskBASKET=bsk.id LEFT JOIN user AS u ON u.id=bsk.id_owner WHERE ugbsk.id_usergroup=0 GROUP BY bsk.id""" if sort == 'name': query += """ ORDER BY bsk.name""" elif sort == 'owner': query += """ ORDER BY u.nickname""" elif sort == 'views': query += """ ORDER BY bsk.nb_views""" elif sort == 'date': query += """ ORDER BY bsk.date_modification""" elif sort == 'items': query += """ ORDER BY items""" else: query += """ ORDER BY bsk.name""" if asc: query += """ ASC""" if sort == """owner""": query += """, u.id""" else: query += """ DESC""" if sort == """owner""": query += """, u.id""" query += """ LIMIT %s, %s""" page = max(0, page) res = run_sql(query, (page, max_number)) return res def is_basket_public(bskid): """Check if the given basket is public. Returns ((0,),) if False, ((1,),) if True.""" query = """ SELECT COUNT(*) FROM usergroup_bskBASKET WHERE id_usergroup=0 AND id_bskBASKET=%s""" params = (bskid,) res = run_sql(query, params) return __wash_sql_count(res) def subscribe(uid, bskid): """Subscribe the given user to the given basket.""" query1 = """SELECT COUNT(*) FROM user_bskBASKET WHERE id_user=%s AND id_bskBASKET=%s""" params1 = (uid, bskid) res1 = run_sql(query1, params1) if res1[0][0]: # The user is either the owner of the basket or is already subscribed. return False else: query2 = """INSERT INTO user_bskBASKET (id_user, id_bskBASKET) VALUES (%s, %s)""" params2 = (uid, bskid) run_sql(query2, params2) return True def unsubscribe(uid, bskid): """Unsubscribe the given user from the given basket.""" query1 = """SELECT COUNT(*) FROM bskBASKET WHERE id_owner=%s AND id=%s""" params1 = (uid, bskid) res1 = run_sql(query1, params1) if res1[0][0]: # The user is the owner of the basket. return False else: query2 = """DELETE FROM user_bskBASKET WHERE id_user=%s AND id_bskBASKET=%s""" params2 = (uid, bskid) res2 = run_sql(query2, params2) if res2: return True else: return False def is_user_subscribed_to_basket(uid, bskid): """Return ((1,),) if the user is subscribed to the given basket or ((0,),) if the user is not subscribed or is the owner of the basket.""" query = """ SELECT COUNT(ubsk.id_bskBASKET) FROM user_bskBASKET AS ubsk JOIN bskBASKET AS bsk ON bsk.id=ubsk.id_bskBASKET AND bsk.id_owner!=ubsk.id_user WHERE ubsk.id_user=%s AND ubsk.id_bskBASKET=%s""" params = (uid, bskid) res = run_sql(query, params) return __wash_sql_count(res) def count_subscribers(uid, bskid): """Returns a (number of users, number of groups, number of alerts) tuple for the given user (uid) and basket (bskid).""" uid = int(uid) bskid = int(bskid) query_groups = """ SELECT count(id_usergroup) FROM usergroup_bskBASKET WHERE id_bskBASKET=%s AND NOT(share_level='NO') GROUP BY id_bskBASKET""" params_groups = (bskid,) res_groups = run_sql(query_groups, params_groups) nb_groups = __wash_sql_count(res_groups) query_users = """ SELECT count(id_user) FROM user_bskBASKET WHERE id_bskBASKET=%s AND id_user!=%s GROUP BY id_bskBASKET""" params_users = (bskid, uid) res_users = run_sql(query_users, params_users) nb_users = __wash_sql_count(res_users) query_alerts = """ SELECT count(id_query) FROM user_query_basket WHERE id_basket=%s GROUP BY id_basket""" params_alerts = (bskid,) res_alerts = run_sql(query_alerts, params_alerts) nb_alerts = __wash_sql_count(res_alerts) return (nb_users, nb_groups, nb_alerts) def get_groups_subscribing_to_basket(bskid): """ get list of (group id, group name, rights) tuples for a given basket Please note that group 0 is used to mean everybody. """ query = """SELECT ugb.id_usergroup, ug.name, ugb.share_level FROM usergroup_bskBASKET ugb LEFT JOIN usergroup ug ON ugb.id_usergroup=ug.id WHERE ugb.id_bskBASKET=%s ORDER BY ugb.id_usergroup""" return run_sql(query, (int(bskid),)) def get_rights_on_public_basket(bskid): """""" query = """ SELECT share_level FROM usergroup_bskBASKET WHERE id_usergroup=0 AND id_bskBASKET=%s""" params = (bskid,) res = run_sql(query, params) return res def count_public_basket_subscribers(bskid): """Return the number of users subscribed to the given public basket.""" query = """ SELECT COUNT(ubsk.id_user) FROM user_bskBASKET AS ubsk JOIN bskBASKET AS bsk ON bsk.id=ubsk.id_bskBASKET AND bsk.id_owner!=ubsk.id_user WHERE ubsk.id_bskBASKET=%s""" params = (bskid,) res = run_sql(query, params) return __wash_sql_count(res) ################################ Notes ######################################## def get_notes(bskid, recid): """Return all comments for record recid in basket bskid.""" query = """ SELECT user.id, user.nickname, bskcmt.title, bskcmt.body, DATE_FORMAT(bskcmt.date_creation, '%%Y-%%m-%%d %%H:%%i:%%s'), bskcmt.priority, bskcmt.id, bskcmt.in_reply_to_id_bskRECORDCOMMENT FROM bskRECORDCOMMENT bskcmt LEFT JOIN user ON (bskcmt.id_user=user.id) WHERE bskcmt.id_bskBASKET=%s AND bskcmt.id_bibrec_or_bskEXTREC=%s ORDER BY bskcmt.reply_order_cached_data """ bskid = int(bskid) recid = int(recid) res = run_sql(query, (bskid, recid)) if res: return res else: return () def get_note(cmtid): """Return comment cmtid as a (author's nickname, author's uid, title, body, date of creation, priority) tuple""" out = () query = """ SELECT user.nickname, user.id, bskcmt.title, bskcmt.body, DATE_FORMAT(bskcmt.date_creation, '%%Y-%%m-%%d %%H:%%i:%%s'), bskcmt.priority FROM bskRECORDCOMMENT bskcmt LEFT JOIN user ON (bskcmt.id_user=user.id) WHERE bskcmt.id=%s """ cmtid = int(cmtid) res = run_sql(query, (cmtid,)) if res: return res[0] return out def save_note(uid, bskid, recid, title, body, reply_to=None): """Save then given note (title, body) on the given item in the given basket.""" if reply_to and CFG_WEBBASKET_MAX_COMMENT_THREAD_DEPTH >= 0: # Check that we have not reached max depth note_ancestors = get_note_ancestors(reply_to) if len(note_ancestors) >= CFG_WEBBASKET_MAX_COMMENT_THREAD_DEPTH: if CFG_WEBBASKET_MAX_COMMENT_THREAD_DEPTH == 0: reply_to = None else: reply_to = note_ancestors[CFG_WEBBASKET_MAX_COMMENT_THREAD_DEPTH - 1] date = convert_datestruct_to_datetext(localtime()) res = run_sql("""INSERT INTO bskRECORDCOMMENT (id_user, id_bskBASKET, id_bibrec_or_bskEXTREC, title, body, date_creation, in_reply_to_id_bskRECORDCOMMENT) VALUES (%s, %s, %s, %s, %s, %s, %s)""", (int(uid), int(bskid), int(recid), title, body, date, reply_to or 0)) if res: new_comid = int(res) parent_reply_order = run_sql("""SELECT reply_order_cached_data from bskRECORDCOMMENT where id=%s""", (reply_to,)) if not parent_reply_order or parent_reply_order[0][0] is None: parent_reply_order = '' else: parent_reply_order = parent_reply_order[0][0] run_sql("""UPDATE bskRECORDCOMMENT SET reply_order_cached_data=%s WHERE id=%s""", (parent_reply_order + get_reply_order_cache_data(new_comid), new_comid)) return int(res) return 0 def delete_note(bskid, recid, cmtid): """Delete a comment on an item of a basket""" query = """ DELETE FROM bskRECORDCOMMENT WHERE id_bskBASKET=%s AND id_bibrec_or_bskEXTREC=%s AND id=%s""" params = (int(bskid), int(recid), int(cmtid)) run_sql(query, params) def get_note_ancestors(cmtid, depth=None): """ Returns the list of ancestors of the given note, ordered from oldest to newest ("top-down": direct parent of cmtid is at last position), up to given depth @param cmtid: the ID of the note for which we want to retrieve ancestors @type cmtid: int @param depth: the maximum of levels up from the given note we want to retrieve ancestors. None for no limit, 1 for direct parent only, etc. @type depth: int @return the list of ancestors @rtype: list """ if depth == 0: return [] res = run_sql("SELECT in_reply_to_id_bskRECORDCOMMENT FROM bskRECORDCOMMENT WHERE id=%s", (cmtid,)) if res: parent_cmtid = res[0][0] if parent_cmtid == 0: return [] parent_ancestors = [] if depth: depth -= 1 parent_ancestors = get_note_ancestors(parent_cmtid, depth) parent_ancestors.append(parent_cmtid) return parent_ancestors else: return [] def note_belongs_to_item_in_basket_p(cmtid, recid, bskid): """Returns 1 (True) if the given note (cmtid) belongs to the given item (recid) and the given basket (bskid) or 0 (False).""" query = """ SELECT COUNT(*) FROM bskRECORDCOMMENT WHERE id=%s AND id_bibrec_or_bskEXTREC=%s AND id_bskBASKET=%s""" params = (cmtid, recid, bskid) res = run_sql(query, params) return __wash_sql_count(res) ########################## Usergroup functions ################################ def get_group_infos(uid): """For each group the user with uid is a member of return the id, name and number of baskets.""" query = """SELECT g.id, g.name, count(ugb.id_bskBASKET) FROM usergroup g LEFT JOIN (user_usergroup ug, usergroup_bskBASKET ugb) ON (g.id=ug.id_usergroup AND g.id=ugb.id_usergroup) WHERE ug.id_user=%s AND NOT(ugb.share_level='NO') AND ug.user_status!=%s GROUP BY g.id ORDER BY g.name""" params = (int(uid), CFG_WEBSESSION_USERGROUP_STATUS['PENDING']) res = run_sql(query, params) return res def count_groups_user_member_of(uid): """Returns the number of groups the user has joined.""" query = """ SELECT COUNT(id_usergroup) FROM user_usergroup WHERE id_user=%s AND user_status!=%s""" params = (int(uid), CFG_WEBSESSION_USERGROUP_STATUS['PENDING']) res = run_sql(query, params) return __wash_sql_count(res) def get_groups_user_member_of(uid): """ Get uids and names of groups user is member of. @param uid: user id (int) @return: a tuple of (group_id, group_name) tuples """ query = """ SELECT g.id, g.name FROM usergroup g JOIN user_usergroup ug ON (g.id=ug.id_usergroup) WHERE ug.id_user=%s and ug.user_status!=%s ORDER BY g.name """ params = (int(uid), CFG_WEBSESSION_USERGROUP_STATUS['PENDING']) res = run_sql(query, params) if res: return res return () ########################## auxilliary functions ############################### def __wash_sql_count(res): """Wash the result of SQL COUNT function and return only an integer.""" if res: return res[0][0] return 0 def __decompress_last(item): """private function, used to shorten code""" item = list(item) item[-1] = decompress(item[-1]) return item def create_pseudo_record(es_title, es_desc, es_url, of="hb"): """Return a pseudo record representation given a title and a description.""" if of == 'hb': record = '\n'.join([es_title, es_desc, es_url]) if of == 'xm': # In case we want to use the controlfield, # the -es_id must be used. #<controlfield tag="001">%s</controlfield> record = """<record> <datafield tag="245" ind1=" " ind2=" "> <subfield code="a">%s</subfield> </datafield> <datafield tag="520" ind1=" " ind2=" "> <subfield code="a">%s</subfield> </datafield> <datafield tag="856" ind1="4" ind2=" "> <subfield code="u">%s</subfield> </datafield> </record>""" % (encode_for_xml(es_title), encode_for_xml(es_desc), es_url) return record def prettify_url(url, char_limit=50, nb_dots=3): """If the url has more characters than char_limit return a shortened version of it keeping the beginning and ending and replacing the rest with dots.""" if len(url) > char_limit: # let's set a minimum character limit if char_limit < 5: char_limit = 5 # let's set a maximum number of dots in relation to the character limit if nb_dots > char_limit/4: nb_dots = char_limit/5 nb_char_url = char_limit - nb_dots nb_char_end = nb_char_url/4 nb_char_beg = nb_char_url - nb_char_end return url[:nb_char_beg] + '.'*nb_dots + url[-nb_char_end:] else: return url