Passed
Branch BonHowi (7c7e4c)
by Bartosz
01:17
created

DatabaseCog.db_count_spot_table()   A

Complexity

Conditions 3

Size

Total Lines 16
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 16
rs 9.65
c 0
b 0
f 0
cc 3
nop 4
1
from discord.ext import commands, tasks
2
from modules.get_settings import get_settings
3
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey, \
4
    BigInteger, update, select, DateTime, delete
5
from sqlalchemy.dialects.mysql import insert
6
import cogs.cogbase as cogbase
7
from datetime import datetime
8
import pandas as pd
9
10
metadata_obj = MetaData()
11
member = Table('member', metadata_obj,
12
               Column('id', BigInteger, primary_key=True),
13
               Column('name', String(50), nullable=False),
14
               Column('display_name', String(50), nullable=False)
15
               )
16
fk_member_id = "member.id"
17
18
spots = Table('spots', metadata_obj,
19
              Column('member_id', BigInteger, ForeignKey(fk_member_id), primary_key=True),
20
              Column('legendary', Integer, default=0),
21
              Column('rare', Integer, default=0),
22
              Column('common', Integer, default=0),
23
              Column('event1', Integer, default=0),
24
              Column('event2', Integer, default=0)
25
              )
26
27
spots_temp = Table('spots_temp', metadata_obj,
28
                   Column('member_id', BigInteger, ForeignKey(fk_member_id), primary_key=True),
29
                   Column('legendary', Integer, default=0),
30
                   Column('rare', Integer, default=0),
31
                   Column('common', Integer, default=0),
32
                   Column('event1', Integer, default=0),
33
                   Column('event2', Integer, default=0)
34
                   )
35
36
warn = Table('warn', metadata_obj,
37
             Column('id', Integer, primary_key=True),
38
             Column('member_id', BigInteger, ForeignKey(fk_member_id)),
39
             Column('reason', String(120), nullable=False),
40
             Column('date', DateTime, nullable=False),
41
             )
42
43
coords = Table('coords', metadata_obj,
44
               Column('id', Integer, primary_key=True),
45
               Column('coords', String(100), nullable=False),
46
               Column('monster_type', String(20), nullable=False)
47
               )
48
49
50
# noinspection PyPropertyAccess
51
class DatabaseCog(cogbase.BaseCog):
52
    user = get_settings("DB_U")
53
    password = get_settings("DB_P")
54
    engine = create_engine(f"mysql+mysqldb://{user}:{password}@localhost/server_database?charset=utf8mb4")
55
    metadata_obj.create_all(engine)
56
    conn = None
57
58
    def __init__(self, base):
59
        super().__init__(base)
60
61
        # Connect to database
62
        self.engine = DatabaseCog.engine
63
        metadata_obj.create_all(self.engine)
64
        self.db_update_loop.start()
65
        self.conn = DatabaseCog.conn
66
67
    # ----- BASE DATABASE OPERATIONS -----
68
69
    # Add or update member in member table
70
    def db_add_update_member(self, _member):
71
        self.conn = self.engine.connect()
72
        stmt = insert(member).values(
73
            id=_member.id, name=_member.name,
74
            display_name=_member.display_name)
75
        do_update_stmt = stmt.on_duplicate_key_update(
76
            name=stmt.inserted.name, display_name=stmt.inserted.display_name
77
        )
78
        self.conn.execute(do_update_stmt)
79
        self.conn.close()
80
81
    # Add or update spots in spots table
82
    def db_add_update_spots(self, spots_table, guild_member):
83
        self.conn = self.engine.connect()
84
        stmt = insert(spots_table).values(
85
            member_id=guild_member.id)
86
        do_update_stmt = stmt.on_duplicate_key_update(member_id=stmt.inserted.member_id)
87
        self.conn.execute(do_update_stmt)
88
        self.conn.close()
89
90
    # Add or refresh all guild members and spots to database
91
    async def db_update(self):
92
        self.conn = self.engine.connect()
93
        guild = self.bot.get_guild(self.bot.guild[0])
94
        dt_string = self.bot.get_current_time()
95
        print(f"({dt_string})\t[{self.__class__.__name__}]: Refreshing member and spots tables")
96
        for guild_member in guild.members:
97
            # Member tables
98
            self.db_add_update_member(guild_member)
99
            # Spots tables
100
            self.db_add_update_spots(spots, guild_member)
101
            self.db_add_update_spots(spots_temp, guild_member)
102
        dt_string = self.bot.get_current_time()
103
        print(f"({dt_string})\t[{self.__class__.__name__}]: Member and spots tables refreshed")
104
        self.conn.close()
105
106
    @tasks.loop(hours=12)
107
    async def db_update_loop(self):
108
        await self.db_update()
109
110
    @db_update_loop.before_loop
111
    async def before_db_update_loop(self):
112
        dt_string = self.bot.get_current_time()
113
        print(f'({dt_string})\t[{self.__class__.__name__}]: Waiting until Bot is ready')
114
        await self.bot.wait_until_ready()
115
116
    # Add member to database on member join
117
    @commands.Cog.listener()
118
    async def on_member_join(self, _member):
119
        self.db_add_update_member(_member)
120
        self.db_add_update_spots(spots, _member)
121
        self.db_add_update_spots(spots_temp, _member)
122
123
    # # Command for loading data from previous bot
124
    # @cog_ext.cog_slash(name="updateSpotsWithOld", guild_ids=cogbase.GUILD_IDS,
125
    #                    description="Update db with old data",
126
    #                    permissions=cogbase.PERMISSION_ADMINS)
127
    # async def db_update_spots_old(self, ctx):
128
    #     self.conn = self.engine.connect()
129
    #     import json
130
    #     with open('server_files/old_base.json', 'r', encoding='utf-8-sig') as fp:
131
    #         old_db = json.load(fp)
132
    #     guild = self.bot.get_guild(self.bot.guild[0])
133
    #
134
    #     for mem_id in old_db:
135
    #         if guild.get_member(int(mem_id)) is None:
136
    #             continue
137
    #
138
    #         stmt = select(spots.c.member_id, spots.c.legendary, spots.c.rare, spots.c.common).where(
139
    #             spots.c.member_id == mem_id)
140
    #         result = self.conn.execute(stmt).fetchall()
141
    #         result = result[0]
142
    #         counter_lege = result[1]
143
    #         counter_rare = result[2]
144
    #         counter_common = result[3]
145
    #
146
    #         counter_lege_old = old_db[mem_id]["type_1"] if "type_1" in old_db[mem_id] else 0
147
    #         counter_rare_old = old_db[mem_id]["type_0"] if "type_0" in old_db[mem_id] else 0
148
    #         counter_common_old = old_db[mem_id]["type_4"] if "type_4" in old_db[mem_id] else 0
149
    #
150
    #         stmt = insert(spots).values(
151
    #             member_id=mem_id, legendary=counter_lege_old,
152
    #             rare=counter_rare_old, common=counter_common_old)
153
    #         do_update_stmt = stmt.on_duplicate_key_update(legendary=stmt.inserted.legendary + counter_lege,
154
    #                                                       rare=stmt.inserted.rare + counter_rare,
155
    #                                                       common=stmt.inserted.common + counter_common)
156
    #         self.conn.execute(do_update_stmt)
157
    #
158
    #         stmt = insert(spots_temp).values(
159
    #             member_id=mem_id, legendary=counter_lege_old,
160
    #             rare=counter_rare_old, common=counter_common_old)
161
    #         do_update_stmt = stmt.on_duplicate_key_update(legendary=stmt.inserted.legendary + counter_lege,
162
    #                                                       rare=stmt.inserted.rare + counter_rare,
163
    #                                                       common=stmt.inserted.common + counter_common)
164
    #         self.conn.execute(do_update_stmt)
165
    #
166
    #     await ctx.send(f"Spot tables updated with old data", delete_after=5.0)
167
    #     dt_string = self.bot.get_current_time()
168
    #     print(f'({dt_string})\t[{self.__class__.__name__}]: Spot tables updated with old data')
169
    #     self.conn.close()
170
171
    # ----- SPOTTING OPERATIONS -----
172
173
    # Update spots tables
174
    @classmethod
175
    async def db_count_spot(cls, _id: int, monster_type: str):
176
        cls.conn = cls.engine.connect()
177
        cls.db_count_spot_table(spots, _id, monster_type)
178
        cls.db_count_spot_table(spots_temp, _id, monster_type)
179
        cls.conn.close()
180
181
    @classmethod
182
    def db_count_spot_table(cls, table, _id: int, monster_type: str):
183
        stmt = select(table.c.member_id, table.c.legendary, table.c.rare, table.c.common,
184
                      table.c.event1,
185
                      table.c.event2).where(
186
            table.c.member_id == _id)
187
        result = cls.conn.execute(stmt)
188
        counter = 0
189
        for nr_of_kills in result.columns(monster_type, 'legendary'):
190
            counter = nr_of_kills[0]
191
        if monster_type == "event1":
192
            values = cls.db_count_spot_table_event(table, _id, monster_type, counter)
193
        else:
194
            values = {f"{monster_type}": counter + 1}
195
        stmt = update(table).where(table.c.member_id == _id).values(values)
196
        cls.conn.execute(stmt)
197
198
    @classmethod
199
    def db_count_spot_table_event(cls, table, _id, monster_type: str, counter: int):
200
        stmt = select(table.c.member_id, table.c.legendary, table.c.rare, table.c.common,
201
                      table.c.event1,
202
                      table.c.event2).where(
203
            table.c.member_id == _id)
204
        result = cls.conn.execute(stmt)
205
        counter_leg = 0
206
        for nr_of_kills_leg in result.columns('legendary'):
207
            counter_leg = nr_of_kills_leg[0]
208
        values = {f"{monster_type}": counter + 1, "legendary": counter_leg + 1}
209
        return values
210
211
    # Save coords from spotting channels to database
212
    @classmethod
213
    async def db_save_coords(cls, _coords: str, _monster_type):
214
        cls.conn = cls.engine.connect()
215
        stmt = insert(coords).values(coords=_coords, monster_type=_monster_type)
216
        cls.conn.execute(stmt)
217
        cls.conn.close()
218
219
    # Clear data from spots_temp table(for events etc)
220
    @classmethod
221
    async def db_clear_spots_temp_table(cls):
222
        cls.conn = cls.engine.connect()
223
        stmt = delete(spots_temp)
224
        cls.conn.execute(stmt)
225
        cls.conn.close()
226
227
    # ----- LEADERBOARD OPERATIONS -----
228
229
    # Return all members' spots
230
    @classmethod
231
    async def db_get_spots_df(cls):
232
        cls.conn = cls.engine.connect()
233
        stmt = select(spots.c.member_id, member.c.display_name, spots.c.legendary, spots.c.rare,
234
                      spots.c.common, spots.c.event1, spots.c.event2
235
                      ).select_from(member
236
                                    ).join(spots, member.c.id == spots.c.member_id)
237
        cls.conn.execute(stmt)
238
        df = pd.read_sql(stmt, cls.conn)
239
        cls.conn.close()
240
        return df
241
242
    # ----- WARN OPERATIONS -----
243
244
    # Add member's warn to database
245
    @classmethod
246
    async def db_add_warn(cls, _member: int, _reason: str):
247
        cls.conn = cls.engine.connect()
248
        stmt = insert(warn).values(member_id=_member, reason=_reason, date=datetime.now())
249
        cls.conn.execute(stmt)
250
        cls.conn.close()
251
252
    # Get member's warns from database
253
    @classmethod
254
    async def db_get_warns(cls, _member: int):
255
        cls.conn = cls.engine.connect()
256
        stmt = select(warn.c.reason, warn.c.date).select_from(member).join(warn, member.c.id == warn.c.member_id).where(
257
            member.c.id == _member)
258
        result = cls.conn.execute(stmt)
259
        date_warn = []
260
        counter = 0
261
        for warns in result.columns("reason", "date"):
262
            reason_with_date = [warns[1], warns[0]]
263
            date_warn.append(reason_with_date)
264
            counter += 1
265
        warns_list = [': \t'.join([str(elem) for elem in sublist]) for sublist in date_warn]
266
        cls.conn.close()
267
        return warns_list, counter
268
269
    # Remove all member's warns
270
    @classmethod
271
    async def db_remove_warns(cls, _member: int):
272
        cls.conn = cls.engine.connect()
273
        stmt = delete(warn).where(warn.c.member_id == _member)
274
        cls.conn.execute(stmt)
275
        cls.conn.close()
276
277
    # ----- MEMBER OPERATIONS -----
278
279
    # Return member's spots
280
    @classmethod
281
    async def db_get_member_stats(cls, _member: int):
282
        cls.conn = cls.engine.connect()
283
        stmt = select(member.c.display_name, spots.c.legendary, spots.c.rare, spots.c.common).select_from(member).join(
284
            spots,
285
            member.c.id == spots.c.member_id).where(spots.c.member_id == _member)
286
        df = pd.read_sql(stmt, cls.conn)
287
        cls.conn.close()
288
        return df
289
290
    # ----- COORDS OPERATIONS -----
291
292
    # Return coords
293
    @classmethod
294
    async def db_get_coords(cls):
295
        cls.conn = cls.engine.connect()
296
        stmt = select(coords.c.id, coords.c.coords, coords.c.monster_type).select_from(coords)
297
        df = pd.read_sql(stmt, cls.conn)
298
        cls.conn.close()
299
        return df
300
301
302
def setup(bot: commands.Bot):
303
    bot.add_cog(DatabaseCog(bot))
304