Explorar o código

backend for bidding, bidding record, re-listing, adding to hashchain, viewing detailed art page (when listed) ready for testing

control %!s(int64=3) %!d(string=hai) anos
pai
achega
89a0ef0c92

+ 68 - 14
app/dashboards.py

@@ -1,21 +1,21 @@
 from flask import Blueprint, render_template, request, flash, redirect, url_for
 from flask_login import login_required, current_user
 from werkzeug.security import generate_password_hash, check_password_hash
-from .models import User, Art, List
+from .models import User, Art, List, Bids
 from . import db
 
 from . import dispatch
 from app.lib import clean_file as cf, tools
 from app.lib import collector
 
-from .forms import UPForm, PicForm, CAForm
+from .forms import UPForm, PicForm, CAForm, BidForm
 
 dashboards = Blueprint('dashboards', __name__)
 
 # Main Pages
 @dashboards.route('/', methods=['GET', 'POST'])
 def market():
-    return_list = collector.market_listing()
+    return_list = collector.join_art_list_table()
 
     if request.method == "POST":
         focus_item = request.form.get('focus_but')
@@ -31,13 +31,18 @@ def market():
 def profile():
     form = UPForm()
     form2 = PicForm()
+    user_bid_hist = collector.user_bid_hist(current_user.id)
 
     if request.method == "POST":
         focus_item = request.form.get('focus_but')
-        u_dbcall = User.query.filter_by(id=current_user.id).first()
-        u_dbcall.focus = focus_item
-        db.session.commit()
-        return redirect(url_for('dashboards.detail'))
+        if focus_item and collector.check_art_listing(focus_item):
+            u_dbcall = User.query.filter_by(id=current_user.id).first()
+            u_dbcall.focus = focus_item
+            db.session.commit()
+            return redirect(url_for('dashboards.detail'))
+        elif focus_item and not collector.check_art_listing(focus_item):
+            pass
+
 
     if form2.validate_on_submit():
         print('passsing')
@@ -73,7 +78,8 @@ def profile():
             my_art = my_art,
             my_creation = my_creation,
             form = form,
-            form2 = form2
+            form2 = form2,
+            ubh = user_bid_hist
             )
 
 @dashboards.route('/create_art', methods=['GET', 'POST'])
@@ -85,6 +91,7 @@ def create():
 
    # check POST req
     if form.validate_on_submit():
+        print('this passes')
         new_art = form.upload.data
         art_name = form.art_name.data
         art_desc = form.art_desc.data
@@ -92,7 +99,7 @@ def create():
         buyout_price = form.buyout_price.data
         close_date = form.close_date.data
 
-        # this may be redundant now that we have WTForms ###
+        # For minting Art
         if tools.check_fields([new_art, art_name, min_price, buyout_price, close_date]):
             if new_art and new_art.filename != '' and cf.allowed_file(new_art.filename):
                 designated_fn = cf.sanitize(new_art.filename)
@@ -100,27 +107,74 @@ def create():
                 dispatch.mint(designated_fn, art_name, art_desc, min_price, buyout_price, close_date)
                 flash('Minted & Listed!', category='success')
 
+        # For re-Listing Art
+        if not new_art:
+            new_art = request.form.get('web_group') # fetch filehash
+            if new_art:
+                # get Art obj from filehash
+                art_obj = collector.get_art_obj(new_art)
+                # dispatch re-list function
+                dispatch.list_item(
+                    art_obj.dname,
+                    art_name,
+                    art_desc,
+                    min_price,
+                    buyout_price,
+                    close_date,
+                    art_obj.filehash
+                    )
+                flash('Listed!', category='success')
+            
+
     return render_template('create_art.html', user = current_user, form = form, av_art = available_art)
 	
-@dashboards.route('/search')
+@dashboards.route('/search', methods=['GET', 'POST'])
 @login_required
 def search():
     return render_template('search.html', user = current_user)
 
-@dashboards.route('/detail')
+@dashboards.route('/detail', methods=['GET', 'POST'])
 @login_required
 def detail():
-
     focus = None
 
     # Collects details of the listing based on the 
     # focus pointer of the user
-    return_list = collector.market_listing()
+    # focus is the return of join_art_list_table in collector
+    return_list = collector.join_art_list_table()
     for item in return_list:
         if item[11] == current_user.focus: # comparing hash
             focus = item
             break
 
     owner_obj = collector.find_user_obj(focus[2])
+    item_bid_hist = collector.item_bid_hist(current_user.focus)
+
+    # New Bid
+    form = BidForm()
 
-    return render_template('detail_art.html', user = current_user, detail = focus, own_uname = owner_obj.username)
+    if form.validate_on_submit():
+        user_bid = form.price.data
+        # checking if bid is at buyout price or more
+        if user_bid and user_bid >= focus[8]:
+            dispatch.enter_bid(user_bid, focus)
+            dispatch.tx_exchange(current_user.focus, focus[6], user_bid)
+            flash('You Bought this piece out! WOAH!', category='success')
+            return redirect(url_for('dashboards.profile'))
+        # checking if bid is higher than minimum bidding price
+        elif user_bid and user_bid > focus[7]:
+            dispatch.enter_bid(user_bid, focus)
+            flash('Bid set! Good luck!', category='success')
+        else:
+            flash('Your Bid Price is too low!', category='error')
+        return redirect(url_for('dashboards.detail'))
+
+
+    return render_template(
+            'detail_art.html',
+            user = current_user,
+            detail = focus,
+            own_uname = owner_obj.username,
+            form = form,
+            ibh = item_bid_hist
+            )

+ 89 - 3
app/dispatch.py

@@ -6,16 +6,16 @@ import os
 from datetime import datetime
 from flask_login import current_user
 
-from .models import Hashchain, Art, List, TX , User
+from .models import Hashchain, Art, List, TX , User, Bids, List
 from . import db
 
 from app.lib import clean_file as cf
-from app.lib import hasher as hsh
+from app.lib import hasher as hsh 
 
 def mint(designated_fn, art_name, art_desc, min_price, buyout_price, close_date):
     # gen filehash
     filehash = hsh.hashfile(f'{cf.UPLOAD_FOLDER}/{designated_fn}')
-    # gen txhash (if bought... or sold, or minted)
+    # gen txhash (if exchanged or minted)
     ddt = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
     dt = sqlalchemy.func.now()
     txstring = f'[{filehash}, {ddt}, {current_user.id}, {current_user.id}, 0.0]' # minting
@@ -66,6 +66,92 @@ def mint(designated_fn, art_name, art_desc, min_price, buyout_price, close_date)
     db.session.commit() 
 
 
+def list_item(designated_fn, art_name, art_desc, min_price, buyout_price, close_date, filehash):
+    # gen txhash
+    ddt = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
+    dt = sqlalchemy.func.now()
+    txstring = f'[{filehash}, {ddt}, {current_user.id}, {current_user.id}, 0.0]' # minting
+    txhash = hsh.gen_txhash(txstring)
+    # fetch prev block hash OR GEN HASH
+    try:
+        prev_bhash = db.session.query(Hashchain).order_by(Hashchain.id.desc()).first().blockhash
+        if prev_bhash is None:
+            prev_bhash = hsh.GENESIS_HASH
+    except:
+        prev_bhash = hsh.GENESIS_HASH
+    # gen block hash add to hashchain
+    new_bhash = hsh.append_tx(txhash, prev_bhash)
+    new_block = Hashchain(blockhash = new_bhash)
+    db.session.add(new_block)
+    # add to TX table (list)
+    new_tx = TX(
+            filehash = filehash,
+            datetime = dt,
+            source_id = current_user.id,
+            destination_id = current_user.id,
+            price = 0.0,
+            txhash = txhash
+            )
+    db.session.add(new_tx)
+    # add to List table
+    new_listing = List(
+            filehash = filehash,
+            seller = current_user.id,
+            min_price = min_price,
+            out_price = buyout_price,
+            timeout = close_date,
+            )
+    db.session.add(new_listing)
+
+    db.session.commit() 
+
+def tx_exchange(filehash, source_id, price):
+    dt = sqlalchemy.func.now()
+    ddt = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
+    txstring = f'[{filehash}, {ddt}, {source_id}, {current_user.id}, {price}]' # exchange
+    txhash = hsh.gen_txhash(txstring)
+    # fetch prev block hash OR GEN HASH
+    try:
+        prev_bhash = db.session.query(Hashchain).order_by(Hashchain.id.desc()).first().blockhash
+        if prev_bhash is None:
+            prev_bhash = hsh.GENESIS_HASH
+    except:
+        prev_bhash = hsh.GENESIS_HASH
+    # gen block hash add to hashchain
+    new_bhash = hsh.append_tx(txhash, prev_bhash)
+    new_block = Hashchain(blockhash = new_bhash)
+    db.session.add(new_block)
+
+    new_tx = TX(
+        filehash = filehash,
+        datetime = dt,
+        source_id = source_id,
+        destination_id = current_user.id,
+        price = price,
+        txhash = txhash
+        )
+
+    # add to TX table (exchange)
+    db.session.add(new_tx)
+
+    # remove from List table
+    List.query.filter_by(filehash = filehash).delete()
+
+    db.session.commit()
+
+def enter_bid(user_bid, focus):
+    # add to Bids table
+    new_bid = Bids(
+        filehash = current_user.focus,
+        buyer = current_user.id,
+        bid_price = user_bid
+            )
+    db.session.add(new_bid)
+    # update List table
+    dbcall = List.query.filter_by(filehash = current_user.focus).first()
+    dbcall.min_price = user_bid
+    db.session.commit()
+
 def save_pp(pp_path):
     new_pppath =  User.query.filter_by(id=current_user.id).first()
     new_pppath.profile_image = pp_path

+ 7 - 3
app/forms.py

@@ -1,5 +1,5 @@
 from flask_wtf import FlaskForm
-from wtforms import StringField, SubmitField, PasswordField, IntegerField, RadioField, TextAreaField, DateField
+from wtforms import StringField, SubmitField, PasswordField, IntegerField, RadioField, TextAreaField, DateField, FloatField
 from flask_wtf.file import FileField, FileAllowed, FileRequired
 from wtforms.validators import DataRequired
 
@@ -32,12 +32,16 @@ class CAForm(FlaskForm):
     art_name = StringField(validators=[DataRequired()])
     art_desc = TextAreaField(validators=[DataRequired()])
     min_price = IntegerField(validators=[DataRequired()])
-    buyout_price = IntegerField(validators=[DataRequired()])
+    buyout_price = FloatField(validators=[DataRequired()])
     close_date = DateField(validators=[DataRequired()])
     upload = FileField(validators=[
-        FileRequired(),
+        #FileRequired(), removing this allows this to be optional
         FileAllowed(['jpg', 'jpeg', 'png'], 'Images only!')
         ])
     submit = SubmitField('Mint Picture')
 
+class BidForm(FlaskForm):
+    price = FloatField(validators=[DataRequired()])
+    submit = SubmitField('Bid')
+
 

+ 49 - 6
app/lib/collector.py

@@ -1,9 +1,10 @@
 # Collects data the retarded way
 
 from flask_login import current_user
-from app.models import User, Art, List
+from sqlalchemy import desc
+from app.models import User, Art, List, Bids
 
-def market_listing():
+def join_art_list_table():
     listings = List.query.all()
     art = Art.query.all()
     user_list = User.query.all()
@@ -33,8 +34,9 @@ def market_listing():
 
 def check_listing():
     # returns list of art available to sell
-    owned_art = Art.query.filter_by(owner=current_user.id).all() 
-    listed_art = List.query.filter_by(seller=current_user.id).all()
+    
+    owned_art = Art.query.filter_by(owner = current_user.id).all() 
+    listed_art = List.query.filter_by(seller = current_user.id).all()
     available_art = list()
 
     # to remove art that is already listed
@@ -42,11 +44,52 @@ def check_listing():
         for la in listed_art:
             if oa.filehash != la.filehash:
                 available_art.append(oa)
+        if not listed_art:
+            available_art.append(oa)
+
 
     return available_art
 
+def check_art_listing(filehash):
+    # checks List table if art is there
+    # if not, block redirection to detailed page.
+    owned_art = Art.query.filter_by(owner = current_user.id).all() 
+    listed_art = List.query.filter_by(seller = current_user.id).all()
+    for oa in owned_art:
+        for la in listed_art:
+            if oa.filehash == la.filehash:
+                return True
+            else:
+                return False
+        if not listed_art:
+            return False
+
 
 def find_user_obj(user_id):
-    user = User.query.filter_by(id=user_id).first()
+    user = User.query.filter_by(id = user_id).first()
     return user
-    
+
+def item_bid_hist(filehash):
+    bids = Bids.query.filter_by(filehash = filehash).order_by(desc(Bids.id)).all()
+    return bids
+
+def user_bid_hist(user_id):
+    bids = Bids.query.filter_by(buyer = user_id).order_by(desc(Bids.id)).all()
+    art = Art.query.all()
+    return_list = list()
+
+    for bid in bids:
+        for a in art:
+            if bid.filehash == a.filehash:
+                return_list.append([
+                    a.name,
+                    bid.bid_date,
+                    bid.bid_price
+                    ])
+
+    return return_list
+
+def get_art_obj(filehash):
+    art = Art.query.filter_by(filehash = filehash).first()
+    return art
+

+ 4 - 4
app/templates/create_art.html

@@ -32,9 +32,9 @@
       <div class="row">
         <div class="col input-group-prepend">
           <label class="input-group-text">File upload</label>
-            {{ form.upload(class="upload_Create_art input-group-text", accept="image/png, image/jpeg") }}
+            {{ form.upload(class="upload_Create_art input-group-text", accept="image/png, image/jpeg, image/jpg", required=False) }}
         </div>
-        <p class="upload_Create_art_position">**<b>PNG's</b>, <b>JPEG's</b> Only**</p>
+        <p class="upload_Create_art_position">**<b>PNGs</b> and <b>JPEGs</b> Only**</p>
       </div>
       <p class="body-column-create_art word_OR_location"><b>OR</b></p>
       <!-- Dropdown Menu-->
@@ -44,7 +44,7 @@
           <select class="btn btn-secondary dropdown-toggle Closing-date-input-size" name="web_group" required aria-invalid="false"> 
             <option value="cng-555">Select here</option>
             {% for art in av_art %}
-            <option name="web_group" value="{{art.name}}">{{art.name}}</option>
+            <option name="web_group" value="{{art.filehash}}">{{art.name}}</option>
             {% endfor %}
           </select>
         </div>
@@ -59,7 +59,7 @@
       </div>
       <!-- Tips for the date to let user know-->
       <div class="row tips_location">
-        <small>Tips:</small>
+        <small>Information:</small>
       </div>
       <div class="row tips_location">
         <small>- The maximum Auction time is 14 days</small>

+ 13 - 18
app/templates/detail_art.html

@@ -9,10 +9,6 @@
         <!-- Art Image-->
         <div class="col-md-6"><img class="card-img-top mb-5 mb-md-0" src="static/repository/{{detail[5]}}" alt="..." /></div>
           <div class="col-md-6">
-            <!-- AuctionEnd Time-->
-            <div class="small mb-1">
-                {{ detail[9] }}
-            </div>
             <!-- Art Name-->
             <h1 class="display-5 fw-bolder">{{ detail[0] }}</h1>
             <!-- Art Description-->
@@ -24,19 +20,23 @@
               <span class="text-decoration-line-through">Minimum Price: {{ detail[7] }} USD</span><br>
               <span class="text-decoration-line-through">Creator: {{ detail[3] }}</span><br>
               <span class="text-decoration-line-through">Current Owner: {{ own_uname }}</span><br>
+              <span class="text-decoration-line-through">Auction End Date: {{ detail[9] }}</span><br>
             </div>
             <!-- Bid button-->
             <div class="d-flex">
               <form method="POST">
-                <input 
+                {{ form.hidden_tag() }}
+                {{ form.price(placeholder="Your Starting Bid Price") }}
+                {{ form.submit() }}
+                <!--<input 
                   type="text" 
                   class="form-control text-center me-3 bidding_price_size" 
                   id="bidding_price"
                   name="bidding_price"
                   placeholder="Enter Bidding Price"
-                />
+                  />
                 <label for="Bidding Price"></label>
-                <button class="btn btn-dark flex-shrink-0 input-submit" type="submit">Bid</button>
+                <button class="btn btn-dark flex-shrink-0 input-submit" type="submit">Bid</button>-->
               </form>
             </div>
           </div>
@@ -56,21 +56,16 @@
     <table class="table">
       <thead class="thead-dark">
         <tr>
-        <th>Bidder Name</th>
-        <th>Bidding Time</th>
-        <th>Bidding Price</th>
+        <th>Bid Date</th>
+        <th>Bid Price</th>
         </tr>
       </thead>
+      {% for bid in ibh %}
       <tr>
-        <td>Patrick Star</td>
-        <td>2022-4-3 15:30:00</td>
-        <td>150</td>
-      </tr>
-      <tr>
-        <td>SpongeBob SquarePants</td>
-        <td>2022-4-2 10:00:00</td>
-        <td>100</td>
+          <td>{{ bid.bid_date }}</td>
+          <td>{{ bid.bid_price }}</td>
       </tr>
+      {% endfor %}
     </table>
     <hr>
   </div>

+ 8 - 11
app/templates/profile.html

@@ -132,21 +132,18 @@
                                 <table class="table">
                                     <thead class="thead-colour">
                                         <tr>
-                                        <th>Bidding Art Item Name</th>
-                                        <th>Bidding Time(Order by DESC)</th>
-                                        <th>Bidding Price</th>
+                                        <th>Item Name</th>
+                                        <th>Bid Time</th>
+                                        <th>Bid Price</th>
                                         </tr>
                                     </thead>
+                                    {% for u in ubh %}
                                     <tr>
-                                        <td>The Krusty Krab</td>
-                                        <td>2022-4-3 15:30:00</td>
-                                        <td>150</td>
-                                    </tr>
-                                    <tr>
-                                        <td>Chum Bucket</td>
-                                        <td>2022-4-2 10:00:00</td>
-                                        <td>100</td>
+                                        <td>{{ u[0] }}</td>
+                                        <td>{{ u[1] }}</td>
+                                        <td>{{ u[2] }}</td>
                                     </tr>
+                                    {% endfor %}
                                 </table>
                             </div>
                         </div>